用于 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 攻击,例如 <span><%= userId %></span>
这不是您的情况。
ESAPI.encoder().encodeForHTMLAttribute(userId)
是您在特定的、狭窄的用例中想要的。这是因为转义规则在 Html 属性中与放置在标签中的数据不同。这应该可以解决您的直接问题。
如果该值将由 JavaScript 独占使用,那么您需要 ESAPI.encoder().encodeForJavaScript(userId)
如果该值将是可呈现的 HTML,则将其发送到将呈现您的标记的 javascript 函数,例如 element.innerHTML = “<HTML> Tags and markup”;
,您需要 <%=Encoder.encodeForJS(Encoder.encodeForHTML(userId))%>
这只是几个例子,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)背景模式不起作用