如何防止JSP / Servlet Web应用程序中的XSS攻击?
Answers:
当(重新)显示用户控制的输入时,可以通过使用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>
这将逃脱这可能malform渲染HTML,如人物<
,>
,"
,'
和&
成HTML / XML实体,如<
,>
,"
,'
和&
。
注意,您不需要在Java(Servlet)代码中对其进行转义,因为它们在那儿是无害的。有些人可能选择在请求处理期间(就像您在Servlet或Filter中所做的那样)而不是响应处理(就像在JSP中那样)对它们进行转义,但是这种方式可能会冒着不必要地对数据进行两次转义的风险(例如,&
变为和&amp;
而不是&
最终,最终用户会看到&
正在显示),或者数据库存储的数据变得不可移植(例如,将数据导出到JSON,CSV,XLS,PDF等完全不需要转义HTML的数据时)。您还将失去社交控制,因为您不再知道用户实际填写了什么。作为站点管理员,您真的很想知道哪些用户/ IP正在尝试执行XSS,以便您可以轻松跟踪他们并采取相应的行动。仅在您确实需要在尽可能短的时间内修复开发不良的旧版Web应用程序的残骸时,才应将请求处理期间的转义用作最新手段。不过,您最终应该重写JSP文件,使其成为XSS安全的。
如果你想重新显示用户控制输入为HTML,其中你想只允许HTML标签,如的特定子集<b>
,<i>
,<u>
,等等,那么你需要通过一个白名单来净化输入。您可以为此使用HTML解析器(如Jsoup)。但是,更好的方法是引入一种人类友好的标记语言,例如Markdown(也在Stack Overflow上使用)。然后,您可以为此使用Markdown解析器(如CommonMark)。它还具有内置的HTML清理功能。另请参阅Markdown或HTML。
服务器端与数据库有关的唯一问题是防止SQL注入。您需要确保不要在SQL或JPQL查询中直接对用户控制的输入进行字符串连接,并且要始终使用参数化查询。用JDBC术语,这意味着您应该使用PreparedStatement
而不是Statement
。用JPA术语,请使用Query
。
一种替代方法是从JSP / Servlet迁移到Java EE的MVC框架JSF。它在所有位置都内置了XSS(和CSRF!)预防功能。另请参见JSF中的CSRF,XSS和SQL注入攻击防护。
"SELECT ... WHERE SOMEVAL = " + someval
而不是像所示的那样使用参数化查询时,才是这种情况。没有人可以防止这种开发人员的错误。
how-to-prevent-xss已被问过几次。您会在StackOverflow中找到很多信息。另外,OWASP网站上有XSS预防备忘单,您应该仔细阅读。
在要使用的库上,OWASP的ESAPI库具有Java风格。您应该尝试一下。除此之外,您使用的每个框架都具有针对XSS的保护。再次,OWASP网站提供了有关大多数流行框架的信息,因此,我建议您浏览他们的网站。
我对OWASP Anti-Samy以及在所有阻止XSS进入的所有Spring Controller上的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;
}
}
您可以从此stackoverflow帖子中获取AspectJ顾问程序
我认为这是一种比c:out更好的方法,特别是如果您使用了大量的javascript。
管理XSS需要多次验证,这些验证来自客户端。
我建议定期使用自动化工具测试漏洞,并修复所发现的问题。一般而言,建议一个库来帮助解决特定漏洞要容易得多,而不是针对所有XSS攻击。
Skipfish是Google一直在研究的开源工具:它发现了很多东西,似乎值得使用。
没有针对XSS的简单易用的解决方案。OWASP ESAPI API对转义提供了一些非常有用的支持,并且它们具有标记库。
我的方法是基本上按照以下方式扩展stuts 2标签。
如果您不想在步骤1中修改类,另一种方法是将ESAPI标签导入到freemarker模板中,然后根据需要进行转义。然后,如果需要在JSP中使用as:property标记,请使用和ESAPI标记进行包装。
我在这里写了更详细的解释。
http://www.nutshellsoftware.org/software/securing-struts-2-using-esapi-part-1-securing-outputs/
我同意转义输入不是理想的。
如果要自动转义所有JSP变量而不必显式包装每个变量,则可以使用EL解析器(如此处所述,包括完整的源代码和示例(JSP 2.0或更高版本),并在此处进行详细讨论:
例如,通过使用上述的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();
}
}
}
再说一次:这只会保护$
。另请参阅其他答案。