Как выполнить кодирование вывода с помощью фильтра для предотвращения XSS?

Я использую следующий код в сервлете:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out=response.getWriter();
    response.setContentType("text/html");


    out.println("<html>");
    out.println("<body>");
    out.println("<script>alert(1)</script>");
    out.println("</body>");
    out.println("</html>");
}

И следующий код для фильтра:

public class SampleFilter implements Filter {
  protected FilterConfig config;

  public void init(FilterConfig config) throws ServletException {
    this.config = config;
  }

  public void destroy() {
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws ServletException, IOException {
      long startTime = System.currentTimeMillis();
    ServletResponse newResponse = response;

    if (request instanceof HttpServletRequest) {
        System.out.println("in filter if1");
      newResponse = new CharResponseWrapper((HttpServletResponse) response);
    }
    System.out.println("after filter if1");
    chain.doFilter(request, newResponse);
    long elapsed = System.currentTimeMillis() - startTime;
    if (newResponse instanceof CharResponseWrapper) {
        System.out.println("in filter if2");
      String text = newResponse.toString();
      if (text != null) {
        text = SampleFilter.HTMLEntityEncode(text);//.toUpperCase();
        response.getWriter().write(text);
      }
    }
    System.out.println("after filter if2");
    config.getServletContext().log(" took " + elapsed + " ms");
    System.out.println(elapsed);
  }

  private static String HTMLEntityEncode(String input) {

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < input.length(); i++) {

          char ch = input.charAt(i);

          if (Character.isLetterOrDigit(ch) || Character.isWhitespace(ch)) {

            sb.append(ch);

          } else {

            sb.append("&#" + (int)ch + ";");

          }

        }

        return sb.toString();

  }

}

Я хочу получить следующие отображаемые данные в браузере:

<script>alert(1)</script>

скорее я получаю

<html>
<body>
<script>alert(1)</script>
</body>
</html>

в браузере.

Любая помощь будет отличной.


person Community    schedule 29.03.2013    source источник


Ответы (4)


Не делайте этого трудным путем. Просто используйте JSP для создания вывода HTML. Библиотека стандартных тегов JSP (JSTL) предлагает встроенные способы защиты данных, контролируемых пользователем, от дыр XSS-атак в виде тега <c:out>. и ${fn:escapeXml()} функция..

<p>Welcome, <c:out value="${user.name}" />!</p>
...
<input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />

Они избегают предопределенных XML-сущностей, таких как < на &gt;, так что это становится абсолютно безвредным.

Сервлеты не предназначены для создания HTML-вывода. Они разработаны с целью контроля запроса/ответа.

Смотрите также:

person BalusC    schedule 31.03.2013
comment
Я хотел бы знать, есть ли способ выполнить кодирование вывода без переделки JSP, поскольку клиент сказал нам использовать фильтр. - person ; 01.04.2013

Пытаясь предотвратить XSS-атаки, вы должны отделить действительный код от потенциально опасных частей и действительных выражений. Для этого существуют разные техники:

Избегание связанных данных. В этом случае вы должны использовать какую-то технологию шаблонов. Все, что определено в шаблоне, считается безопасным. В простейшем случае все связанные данные считаются опасными и поэтому экранируются. Одним из решений, упрощающих эту задачу, является Snippetory. (Да, я разрабатываю это. Вы можете получить его на Sourceforge или репозиторий maven) шаблон может выглядеть так:

 <html>
   <body>
     $attack
     $text
   </body>
 </html>

Тогда код привязки может выглядеть так:

Template page = Syntaxes.FLUYT_X.readResource("template.html")
    .encoding(Encodings.html);
page.set("attack", "<script>alert(0)</script>");
page.set("text", "text <--> escaping");
page.render(response.getWriter());

Однако недостатком является то, что вся обработка вывода должна выполняться правильно. Но я думаю, что для серьезных проектов это самый важный способ.

Теперь некоторые подходы, которые можно использовать после обработки, однако обычно используются в сочетании с экранированием связанных данных для реализации сложных вещей, таких как поле редактора здесь, в Stackoverflow:

Белый список. По сути, вы анализируете данные (возможно, используя парсер html) и избегаете всего, что не является частью тега, который вы поместили в свой белый список. И удалите все атрибуты, которые вы не разрешаете. Это довольно безопасно, но также очень ограничительно. Кроме того, это довольно сложно, поэтому я не могу привести здесь пример.

Черный список: почти то же самое, просто вы пропускаете то, чего нет в вашем списке vlack. Если вы что-то забыли, опасные атаки все еще возможны.

person Sir RotN    schedule 29.03.2013

в вашем случае использование наполнителя невозможно, так как нет способа отделить законный контент от любого контента, который был введен. Эвристическая защита черного ящика от xss может применяться путем фильтрации ввода, а не вывода.

person fdreger    schedule 29.03.2013
comment
Можем ли мы использовать фильтр для выполнения кодирования вывода в целом, независимо от моего кода? - person ; 29.03.2013
comment
кодировка? да. безопасность черного ящика? нет. - person fdreger; 29.03.2013
comment
Но поскольку нет способа отделить законный контент от любого контента, который был внедрен, то как кодирование принесет плоды. - person ; 29.03.2013
comment
Я ничего не говорил о каких-то конкретных фруктах. Я понятия не имею, какие фрукты тебе нужны. Нет ничего, что помешало бы вам кодировать вывод любым способом (base64, rot13, utf16, замена всех «a» на «@», что угодно), но, как я уже сказал, вы не получите никакой защиты от XSS таким образом. . - person fdreger; 29.03.2013

Я реализовал фильтр XSS для REST API Джерси. Код можно легко извлечь и применить к стандартному фильтру Java.

Большинство людей рекомендуют кодировать выходные данные, но поскольку к нашим данным можно получить доступ через API JavaScript, и нет никакой гарантии, что наши клиенты будут отфильтровывать уязвимости XSS, мы решили отфильтровать уязвимости XSS при вводе. Дополнительным преимуществом этого подхода является то, что фильтрация выполняется один раз, а не каждый раз при выводе данных.

Обратите внимание, что фильтр необходимо использовать вместе с аннотацией JSR 303 @SafeHtml, чтобы обеспечить правильную фильтрацию содержимого данных POST.

Я задокументировал это в своем блоге здесь: http://codehustler.org/blog/jersey-cross-site-scripting-xss-filter-for-java-web-apps/

person Alessandro Giannone    schedule 15.05.2013