提问人:newbie 提问时间:4/17/2010 最后编辑:BalusCnewbie 更新时间:10/24/2023 访问量:165427
JSP/Servlet Web 应用程序中的 XSS 防护
XSS prevention in JSP/Servlet web application
答:
如何预防 xss 已经被问了好几次。您会在 StackOverflow 中找到很多信息。此外,OWASP网站有一个XSS预防备忘单,你应该仔细阅读。
在要使用的库上,OWASP 的 ESAPI 库具有 java 风格。你应该尝试一下。除此之外,您使用的每个框架都具有针对 XSS 的一些保护。同样,OWASP网站提供了有关最流行框架的信息,因此我建议浏览他们的网站。
评论
在(重新)显示用户控制的输入时,可以使用 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 格式错误的字符(如 、 、 )和 HTML/XML 实体(如 、 、 和 )。<
>
"
'
&
<
>
"
'
&
请注意,您不需要在 Java (Servlet) 代码中转义它们,因为它们在那里是无害的。有些人可能会选择在请求处理期间转义它们(就像您在 Servlet 或 Filter 中所做的那样)而不是响应处理(就像您在 JSP 中所做的那样),但这样您可能会冒着数据不必要地被双重转义的风险(例如 而不是最终最终用户将看到呈现),或者数据库存储的数据变得不可移植(例如,当将数据导出为 JSON、CSV、XLS、PDF 等时,根本不需要 HTML 转义)。您还将失去社交控制,因为您不再知道用户实际填写了什么。作为站点管理员,您非常想知道哪些用户/IP 正在尝试执行 XSS,以便您可以轻松跟踪它们并采取相应的操作。只有在您确实需要在尽可能短的时间内修复开发不良的遗留 Web 应用程序的火车残骸时,才应将请求处理期间的转义用作最新的手段。不过,您最终应该重写 JSP 文件以使其对 XSS 安全。&
&amp;
&
&
如果您想将用户控制的输入重新显示为 HTML,其中您只想允许 HTML 标记的特定子集,如 、 、 等,那么您需要通过白名单清理输入。为此,您可以使用像 Jsoup 这样的 HTML 解析器。但是,更好的做法是引入一种人性化的标记语言,例如 Markdown(也用于 Stack Overflow)。然后你可以为此使用像 CommonMark 这样的 Markdown 解析器。它还具有内置的 HTML 清理功能。另请参阅 Markdown 或 HTML。<b>
<i>
<u>
请注意,术语“清理”(例如 Jsoup/Markdown/Owasp)确实与术语“转义”(例如“转义”)非常不同。HTML 清理程序基本上会清理包含可能恶意 HTML 的字符串,以便它可以用作安全的 HTML,而无需转义。也就是说,当您实际打算将用户控制的输入从字面上解释为 HTML 时,包括 、 、 等.HTML 等标签。 HTML转义者基本上完全阻止它们被解释,因此它们显示为纯文本。换句话说,您根本不需要事先清理任何您已经要逃脱的 HTML。<c:out>
<div>
<p>
<img>
在服务器端,关于数据库的唯一问题是 SQL 注入预防。您需要确保永远不会在 SQL 或 JPQL 查询中直接对用户控制的输入进行字符串连接,并且始终使用参数化查询。在 JDBC 术语中,这意味着您应该使用 PreparedStatement
而不是 .在 JPA 术语中,使用 Query
。Statement
另一种方法是从 JSP/Servlet 迁移到 Java EE 的 MVC 框架 JSF。它内置了 XSS(和 CSRF!)预防功能,因此您无需手动摆弄和朋友。另请参阅 JSF 中的 CSRF、XSS 和 SQL 注入攻击防护。<c:out>
评论
"SELECT ... WHERE SOMEVAL = " + someval
我建议定期使用自动化工具测试漏洞,并修复它发现的任何漏洞。建议一个库来帮助解决特定漏洞比一般的所有 XSS 攻击要容易得多。
Skipfish是我一直在研究的谷歌开源工具:它找到了很多东西,似乎值得使用。
评论
我个人的观点是,你应该避免使用JSP/ASP/PHP/等页面。而是输出到类似于 SAX 的 API(仅用于调用而不是处理)。这样一来,就有一个单一的层必须创建格式良好的输出。
我很幸运地使用了 OWASP Anti-Samy 和我所有 Spring 控制器上的 AspectJ 顾问,它阻止了 XSS 的进入。
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 顾问
我认为这是一种更好的方法,特别是如果您做很多 javascript,则 c:out。
评论
没有针对 XSS 的简单、开箱即用的解决方案。OWASP ESAPI API 对转义有一些支持,这非常有用,并且它们有标记库。
我的方法基本上是以下列方式扩展 stuts 2 标签。
- 修改 s:property 标签,以便它可以采用额外的属性来说明需要什么样的转义(escapeHtmlAttribute=“true” 等)。这涉及创建新的 Property 和 PropertyTag 类。Property 类使用 OWASP ESAPI API 进行转义。
- 更改 freemarker 模板以使用新版本的 s:property 并设置转义。
如果不想修改步骤 1 中的类,另一种方法是将 ESAPI 标记导入 freemarker 模板并根据需要进行转义。然后,如果需要在 JSP 中使用 s: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 需要来自客户端的多次验证和数据。
- 服务器端的输入验证(表单验证)。有多种方法可以做到这一点。您可以尝试 JSR 303 bean 验证(hibernate validator)或 ESAPI Input Validation 框架。虽然我自己还没有尝试过,但有一个注释可以检查安全的html(@SafeHtml)。事实上,你可以将 Hibernate 验证器与 Spring MVC 一起使用,以进行 bean 验证 -> Ref
- 转义 URL 请求 - 对于所有 HTTP 请求,请使用某种 XSS 过滤器。我为我们的 Web 应用程序使用了以下内容,它负责清理 HTTP URL 请求 - http://www.servletsuite.com/servlets/xssflt.htm
- 转义返回给客户端的数据/html(查看上面@BalusC解释)。
如果你想确保你的操作员不会受到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();
}
}
}
再说一遍:这只会保护 .另请参阅其他答案。$
<%@ page import="org.apache.commons.lang.StringEscapeUtils" %>
String str=request.getParameter("urlParam");
String safeOuput = StringEscapeUtils.escapeXml(str);
评论