JSP/Servlet Web 应用程序中的 XSS 预防

Posted

技术标签:

【中文标题】JSP/Servlet Web 应用程序中的 XSS 预防【英文标题】:XSS prevention in JSP/Servlet web application 【发布时间】:2011-02-09 04:15:10 【问题描述】:

如何防止 JSP/Servlet Web 应用程序中的 XSS 攻击?

【问题讨论】:

如何在不同情况下防止XSS攻击的好帖子发在那里:***.com/questions/19824338/… 【参考方案1】:

当(重新)显示用户控制的输入时,可以通过使用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 格式错误的字符(例如 &amp;lt;&amp;gt;"'&amp;amp;amp;)转义为 HTML/XML entities,例如 &amp;lt;&amp;gt;、@987654346 @、&amp;apos;&amp;amp;amp;

请注意,您不需要在 Java (Servlet) 代码中转义它们,因为它们在那里是无害的。有些人可能会选择在 request 处理期间(就像您在 Servlet 或 Filter 中所做的那样)而不是 response 处理(就像您在 JSP 中所做的那样)转义它们,但是这样您可能会冒险数据不必要地被双重转义(例如&amp;amp;amp;变成&amp;amp;amp;而不是&amp;amp;amp;,最终最终用户会看到&amp;amp;amp;被呈现),或者数据库存储的数据变得不可移植(例如,当将数据导出到JSON、CSV、XLS、PDF 等根本不需要 HTML 转义)。您还将失去社交控制,因为您不再知道用户实际填写了什么。作为站点管理员,您真的很想知道哪些用户/IP 正在尝试执行 XSS,以便您可以轻松跟踪他们并采取相应的行动。在请求处理过程中转义应该并且仅在您确实需要在尽可能短的时间内修复开发不佳的遗留 Web 应用程序的火车残骸时才被用作最新的手段。尽管如此,您最终还是应该重写您的 JSP 文件以保证 XSS 安全。

如果您希望将用户控制的输入重新显示为 HTML,其中您希望仅允许特定的 HTML 标签子集,如 &lt;b&gt;&lt;i&gt;&lt;u&gt; 等,那么您需要清理通过白名单输入。为此,您可以使用像 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。

【讨论】:

仅仅因为您使用的是 Hibernate,并不意味着您可以安全地避免 SQL 注入。例如,请参阅blog.harpoontech.com/2008/10/…。 @chad:这不是真的。只有当您像"SELECT ... WHERE SOMEVAL = " + someval 那样直接在 SQL/HQL/JPQL 查询中将用户控制的输入进行字符串连接时才会出现这种情况,而不是像您展示的那样使用参数化查询。没有一个 ORM 可以防范这种开发人员错误。 我认为您还必须在服务器中进行验证。通过更改 HTTP 参数可以绕过所有验证。有时,您保留的数据可能会被企业应用程序中的其他应用程序使用。有时您无法访问其他应用程序的视图,因此您需要在持久化到数据库之前清理输入。 @Guido:你没有理解问题。 @peater: 是的,当在 JS 代码中放入不受信任的数据时,你需要 JS-encode 而不是 HTML 编码。而且,当将不受信任的数据放入 CSS 代码中时,您需要 CSS 编码而不是 HTML 编码。而且,当将不受信任的数据放入 URL 中时,您需要 URL-encode 而不是 HTML 编码。 HTML 编码只能用于将不受信任的数据放入 HTML 代码中。【参考方案2】:

how-to-prevent-xss 已被多次询问。你会在 *** 中找到很多信息。另外,OWASP website has an XSS prevention cheat sheet 你应该去看看。

在要使用的库上,OWASP's ESAPI library 具有 java 风格。你应该试试看。除此之外,您使用的每个框架都有一些针对 XSS 的保护。同样,OWASP 网站提供有关最流行框架的信息,因此我建议您浏览他们的网站。

【讨论】:

OWASP 备忘单已移至 GitHub。这是 XSS 预防备忘单的链接github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…【参考方案3】:

我在所有阻止 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 *** post 获取 AspectJ 顾问

我认为这是一个比 c:out 更好的方法,特别是如果你做很多 javascript

【讨论】:

通常的做法是在重新显示期间对任何用户控制的数据进行 HTML 转义,而不是在处理 servlet 中提交的数据期间或在数据库中存储期间。如果您在处理提交的数据和/或存储在数据库中的过程中对它进行 HTML 转义,那么它都分布在业务代码和/或数据库中。这只是维护问题,当您在不同的地方执行此操作时,您将面临双重逃逸或更多风险。业务代码和数据库反过来对 XSS 不敏感。只有视图是。然后,您应该只在视野范围内逃避它。 是和不是。尽管一般做法是在展示时逃避,但您可能出于许多原因想要在写入时进行消毒。在某些情况下,您确实希望您的用户输入 HTML 的子集,尽管您可以在显示上进行清理,但这实际上相当慢,甚至会让用户感到困惑。其次,如果您与外部 API 等第三方服务共享数据,这些服务可能会或可能不会自行进行适当的清理。 正如你和我都提到的,“正常做法”是在展示时逃避。您在上面的评论中提到的是更具体的用例,因此需要特定的解决方案。 是的,我也许应该让我的用例更清楚。我主要从事内容管理(HTML 编辑)工作。【参考方案4】:

管理 XSS 需要多次验证,来自客户端的数据。

    服务器端的输入验证(表单验证)。有多种方法可以解决它。您可以尝试 JSR 303 bean 验证(hibernate validator)或ESAPI Input Validation framework。虽然我自己(还)没有尝试过,但是有一个注释可以检查安全的 html (@SafeHtml)。实际上,您可以将 Hibernate 验证器与 Spring MVC 一起用于 bean 验证 -> Ref 转义 URL 请求 - 对于所有 HTTP 请求,使用某种 XSS 过滤器。我为我们的网络应用程序使用了以下内容,它负责清理 HTTP URL 请求 - http://www.servletsuite.com/servlets/xssflt.htm 转义数据/html返回给客户端(看上面@BalusC的解释)。

【讨论】:

【参考方案5】:

我建议使用自动化工具定期测试漏洞,并修复它发现的任何东西。建议一个库来帮助解决特定漏洞比一般的所有 XSS 攻击要容易得多。

Skipfish 是我一直在研究的来自 Google 的开源工具:它发现了很多东西,而且似乎值得使用。

【讨论】:

预防胜于诊断(例如skipfish)然后进行快速修复。 我不同意。没有诊断的预防只是教条。作为 CI 周期的一部分运行诊断以避免“快速修复”问题。【参考方案6】:

没有针对 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/

我同意转义输入并不理想。

【讨论】:

【参考方案7】:

我个人的意见是你应该避免使用 JSP/ASP/php/etc 页面。而是输出到类似于 SAX 的 API(仅设计用于调用而不是处理)。这样一来,只有一个层必须创建格式良好的输出。

【讨论】:

【参考方案8】:

如果您想自动转义 所有 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/

【讨论】:

嗨布拉德,上面的用例是我正在寻找的。你能帮忙解释一下在上述情况下如何防止 xss(foreach one) 我现在唯一真正记得的是使用 EL 解析器 - 这就是我们最终在我们公司使用的。基本上它会自动转义所有内容,如果您真的不想转义,可以将其包裹在&lt;enhance:out escapeXml="false"&gt; 中,详见文章。【参考方案9】:

如果您想确保您的 $ 操作员不会遭受 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();
        

    


再说一遍:这只保护$。另请参阅其他答案。

【讨论】:

以上是关于JSP/Servlet Web 应用程序中的 XSS 预防的主要内容,如果未能解决你的问题,请参考以下文章

Java Web 技术(JSP 和 servlet)中的 AJAX 自动完成文本框

JSP/Servlet Web 学习笔记 DayTwo

JSP/Servlet Web 学习笔记 DayFour

JSP/Servlet Web 学习笔记 DayThree

javaweb之JSP+Servlet

JSP基础知识