用于 XSS 预防的 ESAPI 不起作用

Posted

技术标签:

【中文标题】用于 XSS 预防的 ESAPI 不起作用【英文标题】:ESAPI for XSS prevention not working 【发布时间】:2015-06-06 06:42:11 【问题描述】:

我正在努力解决我们的代码中的跨站点脚本问题,主要是在 JSPS 中。

以下是原代码

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

并且在同一个 Jsp 中

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

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

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

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

------- 更新

非常感谢@avgvstvs。这是非常有见地的。遵循指南,不确定我是否错过了一些东西。代码 -

          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 扫描仍将它们列为漏洞 :-(

【问题讨论】:

XSS 验证不佳是中等风险还是严重风险? 它属于中等风险,我们正计划消除中等风险。 @Eric 我刚刚遵循了备忘单owasp.org/index.php/…,但 Fortify 仍然抱怨 :-( 是的,我不确定为什么它不完全支持该编码。您也可以尝试使用 Validator 类中的 getValidHTML。我仍在调查为什么这种编码不受信任,所以我会在有问题时报告。 您需要共享使用userId 的代码行。一些扫描工具将针对初始分配而不是风险实际所在的位置。此外,如果要在同一页面上的 javascript 上使用相同的值,这可能是一个 XSS 问题,像 Fortify 这样的工具无法发现,因为它无法在运行时攻击页面。在将目标字段标记为误报之前,您应该使用 ZAP 或 burpsuite 之类的工具对目标字段运行一些 XSS 模糊测试。不过,对于这个问题,我需要更多代码。 【参考方案1】:

您需要问的第一个问题应该是“我将这个值交给哪些代码解释器?”

不幸的是,防止 XSS 不是基于配方的任务,而且从外观上看——您的应用程序正在使用脚本,aside from being bad Java practice,这使得 Fortify 等静态代码扫描工具更难为您提供准确的结果。 JSP 在运行时编译,但 Fortify 只扫描源代码。您应该注意,将来应该有提交的任务/故事将 scriptlet 重构为 JSTL——稍后您会感谢我的。那么您可以将 esapi taglibs 用于这些用例。 Fortify 可以很好地扫描纯 Java 代码,而 taglibs 可以做到这一点。

    ESAPI.encoder().encodeForHTML(userId) 只会在有问题的变量将放置在标签之间的用例中保护您免受 XSS 攻击,例如 &lt;span&gt;&lt;%= userId %&gt;&lt;/span&gt; 这不是您的情况。

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

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

    如果该值将是可呈现的 HTML,则将其发送到将呈现您的标记的 javascript 函数,例如 element.innerHTML = “&lt;HTML&gt; Tags and markup”;,您需要 &lt;%=Encoder.encodeForJS(Encoder.encodeForHTML(userId))%&gt;

这只是几个例子,here are a few more--但我的回答最重要的要点是:您需要知道应用程序中每个变量的完整数据流,并且总是编码对于适当的输出上下文。在安全领域,“上下文”意味着“数据正在被传递给新的代码解释器”。如果你很好地实现了这种理解,你将不再需要 Fortify! :-)

增加了复杂性

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

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

对于 Javascript 案例:

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

然后适当地使用这些值。如果您使用 JSTL/taglibs,您可以在正确的位置使用正确的 esapi taglib,而不是拆分成两个单独的 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 的事实,并确保您的 JavaScript 没有做类似的事情。

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

所以我们已经追踪了所有的数据路径,我们已经仔细检查了我们的 JSP 没有被包含在另一个 JSP 中,该 JSP 引入了新的上下文供我们防御,此时我' d 闻到“误报”的味道,但如果我是你,我会联系惠普支持并提出误报问题。除非我可以访问完整的源代码,否则我对您的应用程序的细节知之甚少,无法在这里真正对您有用。因为您正在处理小脚本——Fortify 可能无法跟踪从变量实例化到最终输出的完整数据路径,因此失败很快,因此它至少可以警告您“到目前为止”发现了什么。

【讨论】:

嗨,你的简单易懂的解释给我留下了深刻的印象,我也对你的回答投了赞成票。你能帮我了解一下这两种用法吗?我们也可以在哪里使用以及如何使用。 1.encodeForURL 2.encodeForBase64 ? encodeForURL 是在 URL 中将值用作参数或动态构建 URL 时。 encodeForBase64 更具体一点,基本上你会看到在发送加密数据时,然后通过网络以base64 发送。当你需要它时,你会知道的。【参考方案2】:

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

    实现 Anitsamy 过滤器

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

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

    b.常用属性的正则表达式,如 href、align 等。

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

    使用 ESAPI 库执行输入验证

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

    在上面的代码中,

      “报告名称”是上下文 reportName 是数据字段 ReportNamePattern 是 ESAPI.properties 中定义为 Validator.ReportNamePattern =^[a-zA-Z]1[0-9]6$ 的正则表达式模式 100 是数据字段 reportName 的最大长度 false 是表示不允许空值的标志。

    执行输出编码 正如@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)%>"/>       
    

【讨论】:

哇,好答案!您设法抓住了在 Java 中对抗 XSS 的三个优先事项。正式投票! 谢谢@avgvstvs。感谢您为我指明了正确的方向:-) @Pro :我亲,我已经完成了你的解决方案并且投票也非常有用,但在我的情况下,我直接在我的 jsp 中的元素级别进行属性编码,例如 " style="font-size:12px;font-family:arial,helvetica,sans-serif‌​;" onkeyup = "releaseVersionSearch();" onblink="releaseVersionSearch();" class="modulecontent" /> 当我这样做时,我的页面没有在浏览器上加载 在浏览器控制台选项卡中我可以看到这个错误 net::ERR_INCOMPLETE_CHUNKED_ENCODING 可以请帮助我在这里做错了什么吗?泰。 嗨,我有一个场景,我必须加密或保护整数。 Example //scriplet code 知道如何加密整数或在上面的代码中引发误报。请帮忙,因为我坚持得很糟糕。

以上是关于用于 XSS 预防的 ESAPI 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

用于 cppcheck 的 SublimeLinter(用于 sublime 3)背景模式不起作用

用于查找不同元素的 SQL 查询不起作用

用于基本授权的 ajax 设置标头不起作用

用于原子访问的文件锁定不起作用

用于重新启动 memcached 的 Cron 脚本不起作用

用于更改 href 的 JQuery 脚本不起作用