【发布时间】:2011-02-09 04:15:10
【问题描述】:
如何防止 JSP/Servlet Web 应用程序中的 XSS 攻击?
【问题讨论】:
-
如何在不同情况下防止XSS攻击的好帖子发在那里:stackoverflow.com/questions/19824338/…
标签: java security jsp servlets xss
如何防止 JSP/Servlet Web 应用程序中的 XSS 攻击?
【问题讨论】:
标签: java security jsp servlets xss
当(重新)显示用户控制的输入时,可以通过使用JSTL<c:out>标签或fn:escapeXml()EL函数在JSP中防止XSS。这包括请求参数、标头、cookie、URL、正文等。您从请求对象中提取的任何内容。此外,存储在数据库中的先前请求的用户控制输入需要在重新显示期间进行转义。
例如:
<p><c:out value="${bean.userControlledValue}"></p>
<p><input name="foo" value="${fn:escapeXml(param.foo)}"></p>
这会将可能导致呈现的 HTML 格式错误的字符(例如 &lt;、&gt;、"、' 和 &amp;amp;)转义为 HTML/XML entities,例如 &lt;、&gt;、@987654346 @、&apos; 和 &amp;amp;。
请注意,您不需要在 Java (Servlet) 代码中转义它们,因为它们在那里是无害的。有些人可能会选择在 request 处理期间(就像您在 Servlet 或 Filter 中所做的那样)而不是 response 处理(就像您在 JSP 中所做的那样)转义它们,但是这样您可能会冒险数据不必要地被双重转义(例如&amp;amp;变成&amp;amp;而不是&amp;amp;,最终最终用户会看到&amp;amp;被呈现),或者数据库存储的数据变得不可移植(例如,当将数据导出到JSON、CSV、XLS、PDF 等根本不需要 HTML 转义)。您还将失去社交控制,因为您不再知道用户实际填写了什么。作为站点管理员,您真的很想知道哪些用户/IP 正在尝试执行 XSS,以便您可以轻松跟踪他们并采取相应的行动。在请求处理过程中转义应该并且仅在您确实需要在尽可能短的时间内修复开发不佳的遗留 Web 应用程序的火车残骸时才被用作最新的手段。尽管如此,您最终还是应该重写您的 JSP 文件以保证 XSS 安全。
如果您希望将用户控制的输入重新显示为 HTML,其中您希望仅允许特定的 HTML 标签子集,如 <b>、<i>、<u> 等,那么您需要清理通过白名单输入。为此,您可以使用像 Jsoup 这样的 HTML 解析器。但是,更好的是引入一种对人类友好的标记语言,例如 Markdown(也在 Stack Overflow 上使用)。然后,您可以为此使用像 CommonMark 这样的 Markdown 解析器。它还具有内置的 HTML 清理功能。另见Markdown or HTML。
服务器端唯一关心的数据库是SQL injection 预防。您需要确保从不直接在 SQL 或 JPQL 查询中对用户控制的输入进行字符串连接,并且始终使用参数化查询。在 JDBC 术语中,这意味着您应该使用 PreparedStatement 而不是 Statement。在 JPA 术语中,使用 Query。
另一种方法是从 JSP/Servlet 迁移到 Java EE 的 MVC 框架JSF。它在所有地方都有内置的 XSS(和 CSRF!)预防。另见CSRF, XSS and SQL Injection attack prevention in JSF。
【讨论】:
"SELECT ... WHERE SOMEVAL = " + someval 那样直接在 SQL/HQL/JPQL 查询中将用户控制的输入进行字符串连接时才会出现这种情况,而不是像您展示的那样使用参数化查询。没有一个 ORM 可以防范这种开发人员错误。
how-to-prevent-xss 已被多次询问。你会在 StackOverflow 中找到很多信息。另外,OWASP website has an XSS prevention cheat sheet 你应该去看看。
在要使用的库上,OWASP's ESAPI library 具有 java 风格。你应该试试看。除此之外,您使用的每个框架都有一些针对 XSS 的保护。同样,OWASP 网站提供有关最流行框架的信息,因此我建议您浏览他们的网站。
【讨论】:
我在所有阻止 XSS 进入的 Spring 控制器上使用 OWASP Anti-Samy 和 AspectJ 顾问非常幸运。
public class UserInputSanitizer {
private static Policy policy;
private static AntiSamy antiSamy;
private static AntiSamy getAntiSamy() throws PolicyException {
if (antiSamy == null) {
policy = getPolicy("evocatus-default");
antiSamy = new AntiSamy();
}
return antiSamy;
}
public static String sanitize(String input) {
CleanResults cr;
try {
cr = getAntiSamy().scan(input, policy);
} catch (Exception e) {
throw new RuntimeException(e);
}
return cr.getCleanHTML();
}
private static Policy getPolicy(String name) throws PolicyException {
Policy policy =
Policy.getInstance(Policy.class.getResourceAsStream("/META-INF/antisamy/" + name + ".xml"));
return policy;
}
}
您可以从this stackoverflow post 获取 AspectJ 顾问
我认为这是一个比 c:out 更好的方法,特别是如果你做很多 javascript。
【讨论】:
管理 XSS 需要多次验证,来自客户端的数据。
【讨论】:
我建议使用自动化工具定期测试漏洞,并修复它发现的任何东西。建议一个库来帮助解决特定漏洞比一般的所有 XSS 攻击要容易得多。
Skipfish 是我一直在研究的来自 Google 的开源工具:它发现了很多东西,而且似乎值得使用。
【讨论】:
没有针对 XSS 的简单、开箱即用的解决方案。 OWASP ESAPI API 有一些非常有用的转义支持,并且他们有标签库。
我的方法基本上是通过以下方式扩展 stuts 2 标签。
如果您不想在步骤 1 中修改类,另一种方法是将 ESAPI 标记导入 freemarker 模板并根据需要进行转义。然后,如果您需要在 JSP 中使用 s:property 标记,请使用 ESAPI 标记将其包装起来。
我在这里写了更详细的解释。
http://www.nutshellsoftware.org/software/securing-struts-2-using-esapi-part-1-securing-outputs/
我同意转义输入并不理想。
【讨论】:
我个人的意见是你应该避免使用 JSP/ASP/PHP/etc 页面。而是输出到类似于 SAX 的 API(仅设计用于调用而不是处理)。这样一来,只有一个层必须创建格式良好的输出。
【讨论】:
如果您想自动转义 所有 JSP 变量而无需显式包装每个变量,您可以使用 EL 解析器 as detailed here with full source and an example (JSP 2.0 or newer),并更详细地讨论 here:
例如,通过使用上面提到的 EL 解析器,您的 JSP 代码将保持原样,但每个变量都会被解析器自动转义
...
<c:forEach items="${orders}" var="item">
<p>${item.name}</p>
<p>${item.price}</p>
<p>${item.description}</p>
</c:forEach>
...
如果你想在 Spring 中默认强制转义,你也可以考虑这个,但它不会转义 EL 表达式,只是标记输出,我认为:
http://forum.springsource.org/showthread.php?61418-Spring-cross-site-scripting&p=205646#post205646
注意:另一种使用 XSL 转换来预处理 JSP 文件的 EL 转义方法可以在这里找到:
http://therning.org/niklas/2007/09/preprocessing-jsp-files-to-automatically-escape-el-expressions/
【讨论】:
<enhance:out escapeXml="false"> 中,详见文章。
如果您想确保您的 $ 操作员不会遭受 XSS 黑客攻击,您可以实现 ServletContextListener 并在那里进行一些检查。
完整的解决方案在:http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html
@WebListener
public class EscapeXmlELResolverListener implements ServletContextListener {
private static final Logger LOG = LoggerFactory.getLogger(EscapeXmlELResolverListener.class);
@Override
public void contextInitialized(ServletContextEvent event) {
LOG.info("EscapeXmlELResolverListener initialized ...");
JspFactory.getDefaultFactory()
.getJspApplicationContext(event.getServletContext())
.addELResolver(new EscapeXmlELResolver());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOG.info("EscapeXmlELResolverListener destroyed");
}
/**
* {@link ELResolver} which escapes XML in String values.
*/
public class EscapeXmlELResolver extends ELResolver {
private ThreadLocal<Boolean> excludeMe = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};
@Override
public Object getValue(ELContext context, Object base, Object property) {
try {
if (excludeMe.get()) {
return null;
}
// This resolver is in the original resolver chain. To prevent
// infinite recursion, set a flag to prevent this resolver from
// invoking the original resolver chain again when its turn in the
// chain comes around.
excludeMe.set(Boolean.TRUE);
Object value = context.getELResolver().getValue(
context, base, property);
if (value instanceof String) {
value = StringEscapeUtils.escapeHtml4((String) value);
}
return value;
} finally {
excludeMe.remove();
}
}
@Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return null;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base){
return null;
}
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value){
throw new UnsupportedOperationException();
}
}
}
再说一遍:这只保护$。另请参阅其他答案。
【讨论】: