Java 用于 XSS 预防的 ESAPI 不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29393828/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ESAPI for XSS prevention not working
提问by Pro
I am working on fixing Cross site scripting issues in our code mainly in JSPS.
我正在主要在 JSPS 中修复我们代码中的跨站点脚本问题。
Below is the original code
下面是原代码
//scriplet code
<% String userId = request.getParameter("sid");
...%>
and in the same Jsp they have
并且在同一个 Jsp 中
<input type = hidden name = "userID" value = "<%= userId %>" />
I have made changes to include esapi-2.1.0.jar in lib and ESAPI.properties, validation.properties in classpath. Then made below changes to scriplet code to fix the above code
我已经进行了更改以在 lib 中包含 esapi-2.1.0.jar 并在类路径中包含 ESAPI.properties、validation.properties。然后对脚本代码进行以下更改以修复上述代码
//scriplet code
<% String userId = ESAPI.encoder().encodeForHTML(request.getParameter("sid"));
...%>
I thought this would fix the issue but when I scan my code using Fortify, these lines are again highlighted as having XSS issue. Please help if you guys have any idea on how this should be handled. Thanks.
我认为这可以解决问题,但是当我使用 Fortify 扫描我的代码时,这些行再次突出显示为存在 XSS 问题。如果你们对如何处理这件事有任何想法,请提供帮助。谢谢。
------- UPDATE
- - - - 更新
Thanks a lot @avgvstvs. This is very insightful.Follwd guidelines, Not sure if I am missng somethn. Code -
非常感谢@avgvstvs。这是非常有见地的。遵循指南,不确定我是否错过了某些东西。代码 -
String userSID=ESAPI.encoder().encodeForHTMLAttribute(request.getHeader("janus_sid")); session.setAttribute("username",userSID);<input type=hidden name="USERNAME" value="<%= userSID %>"
And for another varibale debug, below is the usage
对于另一个变量调试,下面是用法
String debugFlag = ESAPI.encoder().encodeForJavaScript(request.getParameter("debug"));var debugFlag = "<%= debugFlag%>";if(debugFlag == "y"){
document.title= title + " (" + host + ")";
defaultAppTitle = title + " (" + host + ")";
}
Latest Fortify scan still lists them as vulnerabilities :-(
最新的 Fortify 扫描仍将它们列为漏洞 :-(
采纳答案by Pro
Thanks for help guys. Finally figured out a solution to prevent XSS issue and pass Fortify static code analysis. I have used ESAPI together with Anitsamy library. Here are the 3 main changes required.
谢谢你们的帮助。终于想出了防止XSS问题并通过Fortify静态代码分析的解决方案。我将 ESAPI 与 Anitsamy 库一起使用。以下是所需的 3 个主要更改。
Implement Anitsamy Filter
Add a new filter and override request methods getParameter , getParameterValues to strip out any suspicious tags in the request. Filter loads a policy file where we define our rules like
a. tags which needs to be removed from the requests ( tags like , etc)
b. Regexs for common attributes like href, align etc.
实施 Anitsamy 过滤器
添加新过滤器并覆盖请求方法 getParameter 、 getParameterValues 以去除请求中的任何可疑标签。过滤器加载一个策略文件,我们在其中定义我们的规则,例如
一种。需要从请求中删除的标签(标签等)
湾 常见属性的正则表达式,如 href、align 等。
Example for implementation of filter is here http://barrypitman.com/2011/04/14/using-input-validation-XSS/
过滤器的实现示例在这里http://barrypitman.com/2011/04/14/using-input-validation-XSS/
Perform input validation using ESAPI library
String reportName = request.getParameter("reportName"); ESAPI.validator().getValidInput("Report Name", reportName, "ReportNamePattern", 100, false);
In above code,
- "Report Name" is the context
- reportName is the data field
- ReportNamePattern is the regex pattern defined in ESAPI.properties as Validator.ReportNamePattern =^[a-zA-Z]{1}[0-9]{6}$
- 100 is max length for data field reportName
- false is a flag to say null value is not allowed.
Perform output encoding
As pointed by @avgvstvs, output encoding is also a must.If reportName field is to be used in HTML, below is how to encode
<tr> <th> Report : <%=ESAPI.encoder().encodeForHTML(reportName)%> </th> </tr>
If reportName field is to be used in javascript code , below is how to encode
var reportName = "<%= ESAPI.encoder().encodeForJavaScript(reportName)%>";
If reportName field is to be used in HTML Attribute, below is how to encode
<input type=hidden name="USERNAME" value="<%=ESAPI.encoder().encodeForHTMLAttribute (reportName)%>"/>
使用 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)%>"/>
回答by avgvstvs
The first question you need to ask should be "What code interpreters am I handing this value off to?"
您需要问的第一个问题应该是“我要将这个值交给哪些代码解释器?”
Preventing XSS unfortunately isn't a recipe-based task, and from the looks of it--your application is using scriptlets, which aside from being bad Java practice, makes it much more difficult for static code-scanning tools like Fortify to give you accurate results. JSPs are compiled at runtime, but Fortify scans the source only. You should note that there should be future tasks/stories filed to refactor scriptlets to JSTL--you'll thank me for that later. THEN you can use esapi taglibs for these use cases. Fortify does an okay job of scanning pure Java code, and taglibs give you that.
不幸的是,防止 XSS 不是一项基于配方的任务,从它的外观来看——您的应用程序正在使用 scriptlet,除了糟糕的 Java 实践之外,这使得 Fortify 之类的静态代码扫描工具更难以为您提供准确的结果。JSP 在运行时编译,但 Fortify 仅扫描源。您应该注意到,将来应该提交一些任务/故事来将 scriptlet 重构为 JSTL——稍后您会为此感谢我。那么您可以将 esapi taglibs 用于这些用例。Fortify 在扫描纯 Java 代码方面做得不错,而 taglibs 可以为您提供这一点。
ESAPI.encoder().encodeForHTML(userId)
is only going to protect you from XSS in the use case where the variable in question is going to be placed between tags, like<span><%= userId %></span>
This isn't the case you have.ESAPI.encoder().encodeForHTMLAttribute(userId)
is what you want in your particular, narrow use-case. This is because escaping rules are different in Html attributes than they are for data placed within tags. This should fix your immediate problem.If the value is going to be used exclusively by JavaScript, then you want
ESAPI.encoder().encodeForJavaScript(userId)
If the value is going to be renderable HTML being sent to a javascript function that will render your markup, like
element.innerHTML = “<HTML> Tags and markup”;
you want<%=Encoder.encodeForJS(Encoder.encodeForHTML(userId))%>
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))%>
This is just a few examples, here are a few more--but the overriding gist of my answer is this: You need to know the complete flow of data for every variable in your application and alwaysencode for the appropriate output context. And in the security world, "context" means "Data is being handed off to a new code interpreter." If you implement this understanding well, you won't need Fortify anymore! :-)
这只是几个例子,这里还有一些——但我的回答的首要要点是:您需要了解应用程序中每个变量的完整数据流,并始终针对适当的输出上下文进行编码。在安全领域,“上下文”意味着“数据正在被移交给新的代码解释器”。如果您很好地实现了这种理解,您将不再需要 Fortify!:-)
Added complexity
增加了复杂性
In your comment you noted that the value is later being used by JavaScript. So the correct solution in this case would likely to be to fork two different variables, each one set for the proper encoding. For the HTML Attribute case:
在您的评论中,您注意到 JavaScript 稍后会使用该值。因此,在这种情况下,正确的解决方案可能是派生两个不同的变量,每个变量都设置为正确的编码。对于 HTML 属性情况:
String escapedHtmlUserId = ESAPI.encoder().encodeForHTMLAttribute(userId);
For the Javascript case:
对于 Javascript 案例:
String escapedJSUserId = ESAPI.encoder().encodeForJavaScript(userId);
Then use the values appropriately. If you used JSTL/taglibs, you could just use the right esapi taglib in the right place instead of splitting into two separate scriptlet variables.
然后适当地使用这些值。如果你使用 JSTL/taglibs,你可以在正确的地方使用正确的 esapi taglib,而不是分成两个单独的 scriptlet 变量。
AND ONE MORE THING
还有一件事情
From the comment on the OP:
来自对 OP 的评论:
We have an initial scriptlet declaration:
我们有一个初始的 scriptlet 声明:
<% String userId = ESAPI.encoder().encodeForHTML(request.getParameter("sid"));
...%>
and it is used later in a javascript function:
稍后在 javascript 函数中使用它:
<%= ESAPI.encoder().encodeForJavascripl(userId)%>`
Just to point out, as presented, the userId
variable stops being raw text on initialization. Effectively, the encoding for javascript becomes this:
只是要指出,正如所介绍的,userId
变量在初始化时不再是原始文本。实际上,javascript 的编码变成了这样:
<% ESAPI.encoder().encodeForJavascript(ESAPI.encoder().encodeForHTML(request.getParameter("sid")));
...%>
If the javascript is going to be rendering HTML using *.innerHTML() or *.outerHTML() this is fine. Otherwise, just note that the input has changed from its raw state.
如果 javascript 将使用 *.innerHTML() 或 *.outerHTML() 呈现 HTML,这很好。否则,只需注意输入已从其原始状态更改。
Also, watch for the fact some JavaScript can undo what you've doneand ensure your JavaScript isn't doing something similar.
此外,请注意某些JavaScript 可以撤消您所做的操作,并确保您的 JavaScript 不会做类似的事情。
==========More added code, more questions========
==========更多代码,更多问题========
So we've traced all our data paths, we've double-checked that our JSP in question isn't being included in a different JSP that introduces new contexts for us to defend against, it's at this point where I'd smell "false positive," but if I were you I'd contact HP support and raise the issue for a false positive. I don't know enough of the specifics of your application to really be of much more use to you here unless I had access to the full source. Because you're dealing with scriptlets--its possible that Fortify can't trace the full data path from variable instantiation to eventual output, so is failing fast so it can at least warn you as to what its found "so far."
因此,我们已经跟踪了所有数据路径,并仔细检查了我们有问题的 JSP 是否包含在不同的 JSP 中,该 JSP 引入了新的上下文供我们防御,在这一点上我会闻到“误报”,但如果我是您,我会联系 HP 支持并提出误报问题。除非我可以访问完整的源代码,否则我对您的应用程序的具体细节了解得不够多,因此在这里对您真正有用。因为您正在处理 scriptlet - Fortify 可能无法跟踪从变量实例化到最终输出的完整数据路径,因此失败很快,因此它至少可以警告您“到目前为止”发现了什么。