如何使用过滤器执行输出编码以防止XSS



我在servlet中使用以下代码:

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>

rather I am getting

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

别这么辛苦了。只需使用JSP生成HTML输出。JSP标准标记库(JSTL)提供了内置的方法,以<c:out>标记和${fn:escapeXml()}函数的形式将用户控制的数据从XSS攻击漏洞中逃脱。

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

它们将通过&gt;转义像<这样的预定义XML实体,因此它变得完全无害。

servlet不是为生成HTML输出而设计的。它们被设计用于控制请求/响应。

参见:

    JSP/Servlet web应用程序中的XSS防范

当尝试防止XSS攻击时,您必须将有效代码与潜在的危险部分从有效表达式中分离出来。有不同的技术可以实现这一点:

转义绑定数据:在这种情况下,您必须使用某种模板技术。模板中定义的任何内容都被认为是安全的。在最简单的情况下,所有绑定的数据都被认为是危险的,因此被转义。一个简单的解决方案是Snippetory。(是的,我养成了这个习惯。你可以从Sourceforge或maven repo获得它)模板可能看起来像这样:

 <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解析器)并转义不属于白清单上标记的所有内容。删除所有你不允许的属性。这是相当安全的,但也有很大的限制。此外,它非常复杂,所以我不能在这里提供一个例子。

黑名单:几乎一样,只是你让你的黑名单上没有的东西通过。如果你忘记了什么危险的攻击仍然是可能的。

在您的情况下,使用填充是不可能的,因为没有办法将合法内容与任何已注入的内容分开。针对XSS的启发式黑盒防御可以通过过滤输入而不是输出来应用。

我已经为Jersey REST API实现了一个XSS过滤器。可以很容易地提取代码并将其应用于标准Java Filter。

大多数人建议对输出进行编码,但是由于我们的数据可以通过JavaScript API访问,并且无法保证我们的客户会过滤掉XSS漏洞,因此我们选择在输入时过滤掉XSS漏洞。这种方法的另一个好处是,过滤只进行一次,而不是每次输出数据时都进行过滤。

注意,过滤器需要与JSR 303的@SafeHtml注释一起使用,以确保正确过滤POST数据的内容。

我在我的博客上记录了这一点:http://codehustler.org/blog/jersey-cross-site-scripting-xss-filter-for-java-web-apps/

最新更新