用于 XSS 预防的 ESAPI 不起作用

ESAPI for XSS prevention not working

我主要在 JSPS 中修复我们代码中的跨站点脚本问题。

下面是原代码

 //scriplet code
    <% String userId = request.getParameter("sid"); 
    ...%>

并且在同一个 Jsp 他们有

     <input type = hidden name = "userID" value = "<%= userId %>" />

我进行了更改以在 lib 中包含 esapi-2.1.0.jar 并在类路径中包含 ESAPI.properties、validation.properties。然后对 scriplet 代码进行以下更改以修复上述代码

      //scriplet code
    <% String userId = ESAPI.encoder().encodeForHTML(request.getParameter("sid")); 
    ...%>

我认为这会解决问题,但当我使用 Fortify 扫描我的代码时,这些行再次突出显示为存在 XSS 问题。如果你们对如何处理有任何想法,请提供帮助。谢谢。

--------更新

非常感谢@avgvstvs。这是非常 insightful.Follwd 的指导方针,不确定我是否遗漏了什么。代码-

          String              userSID=ESAPI.encoder().encodeForHTMLAttribute(request.getHeader("janus_sid")); session.setAttribute("username",userSID);<input type=hidden name="USERNAME" value="<%= userSID %>"

对于另一个可变调试,下面是用法

       String debugFlag =  ESAPI.encoder().encodeForJavaScript(request.getParameter("debug"));var debugFlag = "<%= debugFlag%>";if(debugFlag == "y"){       
        document.title=   title + " (" + host + ")";
        defaultAppTitle = title + " (" + host +  ")";           
    }                                                           

最新的 Fortify 扫描仍然将它们列为漏洞:-(

你要问的第一个问题应该是 "What code interpreters am I handing this value off to?"

不幸的是,防止 XSS 并不是一项基于配方的任务,而且从它的外观来看——您的应用程序正在使用 scriptlet,这 aside from being bad Java practice 使得静态代码扫描工具(例如Fortify 为您提供准确的结果。 JSPs 在运行时编译,但 Fortify 仅扫描源代码。您应该注意,未来应该有 tasks/stories 提交来将 scriptlet 重构为 JSTL——您稍后会为此感谢我。那么您可以将 esapi 标签库用于这些用例。 Fortify 可以很好地扫描纯 Java 代码,而 taglibs 可以做到这一点。

  1. ESAPI.encoder().encodeForHTML(userId) 只会在相关变量将放置在标签之间的用例中保护您免受 XSS 攻击,例如 <span><%= userId %></span> 这不是你的情况。

  2. ESAPI.encoder().encodeForHTMLAttribute(userId) 是您在特定的狭窄用例中想要的。这是因为 Html 属性中的转义规则与放置在标签中的数据的转义规则不同。这应该可以解决您眼前的问题。

  3. 如果该值将由 Java脚本独占使用,那么您需要 ESAPI.encoder().encodeForJavaScript(userId)

  4. 如果值是可渲染的 HTML 被发送到 javascript 函数来渲染你的标记,比如 element.innerHTML = “<HTML> Tags and markup”; 你想要 <%=Encoder.encodeForJS(Encoder.encodeForHTML(userId))%>

这只是几个例子,here are a few more——但我回答的要点是:您需要了解应用程序中每个变量的完整数据流,总是 编码适当的输出上下文。在安全领域,"context" 意味着 "Data is being handed off to a new code interpreter." 如果你很好地理解了这一点,你就不再需要 Fortify 了! :-)

增加了复杂性

在您的评论中,您注意到该值后来被 JavaScript 使用。因此,在这种情况下,正确的解决方案可能是分叉两个不同的变量,每个变量都设置为正确的编码。对于 HTML 属性案例:

String escapedHtmlUserId = ESAPI.encoder().encodeForHTMLAttribute(userId);

对于 Java 脚本案例:

String escapedJSUserId = ESAPI.encoder().encodeForJavaScript(userId);

然后适当地使用这些值。如果您使用 JSTL/taglibs,您可以在正确的位置使用正确的 esapi 标签库,而不是拆分成两个单独的 scriptlet 变量。

还有一件事

来自对 OP 的评论:

我们有一个初始的 scriptlet 声明:

<% String userId = ESAPI.encoder().encodeForHTML(request.getParameter("sid")); 
...%>

稍后在 javascript 函数中使用:

<%= ESAPI.encoder().encodeForJavascripl(userId)%>`

只是指出,如前所述,userId 变量在初始化时不再是原始文本。实际上,javascript 的编码变成了这样:

<% ESAPI.encoder().encodeForJavascript(ESAPI.encoder().encodeForHTML(request.getParameter("sid"))); 
...%>

如果 javascript 将使用 *.innerHTML() 或 *.outerHTML() 渲染 HTML,这很好。否则,只需注意输入已从其原始状态更改。

此外,注意一些 JavaScript can undo what you've done 的事实并确保您的 Java 脚本没有做类似的事情。

==========更多补充代码,更多问题========

所以我们已经跟踪了所有数据路径,我们仔细检查了我们的 JSP 是否包含在不同的 JSP 中,这为我们引入了新的上下文防御,此时我会闻到 "false positive," 但如果我是你,我会联系 HP 支持并提出误报问题。除非我可以访问完整的源代码,否则我对您的应用程序的细节了解得不够多,无法真正为您提供更多帮助。因为您正在处理 scriptlet——Fortify 可能无法跟踪从变量实例化到最终输出的完整数据路径,所以它很快就会失败,因此它至少可以警告您发现了什么 "so far."

感谢大家的帮助。终于想出了一个解决方案来防止 XSS 问题并通过 Fortify 静态代码分析。我将 ESAPI 与 Anitsamy 库一起使用。以下是需要进行的 3 项主要更改。

  1. 实施 Anitsamy 过滤器

    添加新过滤器并覆盖请求方法 getParameter 和 getParameterValues 以去除请求中的任何可疑标记。过滤器加载一个策略文件,我们在其中定义我们的规则,例如

    一个。需要从请求中删除的标签(诸如 等标签)

    b。 href、align 等常见属性的正则表达式

过滤器的实现示例在这里http://barrypitman.com/2011/04/14/using-input-validation-XSS/

  1. 使用 ESAPI 库执行输入验证

     String reportName = request.getParameter("reportName");
     ESAPI.validator().getValidInput("Report Name", 
                                      reportName, "ReportNamePattern", 100, false);
    

    在上面的代码中,

    1. "Report Name" 是上下文
    2. reportName 是数据字段
    3. ReportNamePattern 是在 ESAPI.properties 中定义为 Validator.ReportNamePattern =^[a-zA-Z]{1}[0-9]{6}$
    4. 的正则表达式模式
    5. 100 是数据字段 reportName 的最大长度
    6. false 是一个标志,表示不允许空值。
  2. 执行输出编码
    正如@avgvstvs 所指出的,输出编码也是必须的。

    如果要在HTML中使用reportName字段,下面是如何编码

    <tr> <th> Report :     <%=ESAPI.encoder().encodeForHTML(reportName)%> </th> </tr>
    

    如果要在 javascript 代码中使用 reportName 字段,下面是如何编码

     var reportName = "<%= ESAPI.encoder().encodeForJavaScript(reportName)%>";
    

    如果要在HTML属性中使用reportName字段,下面是如何编码

    <input type=hidden name="USERNAME" value="<%=ESAPI.encoder().encodeForHTMLAttribute
        (reportName)%>"/>