【问题标题】:How to prevent my web app from CSRF(Cross site request forgery) in javajava - 如何防止我的Web应用程序在Java中使用CSRF(跨站点请求伪造)
【发布时间】:2016-10-10 03:53:33
【问题描述】:

我正在尝试防止我的 Web 应用程序遭受 CSRF(跨站点请求伪造) 我点击了这个链接Link for CSRF

这是我尝试过的。 为了在 Java 中实现这种机制,我选择使用两个过滤器,一个为每个请求创建盐,另一个验证它。由于用户请求和后续应该验证的 POST 或 GET 不一定按顺序执行,因此我决定使用基于时间的缓存来存储有效盐字符串的列表。

第一个过滤器,用于为请求生成一个新的盐并存储在缓存中,可以这样编码:

public class LoadSalt implements Filter{


    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        // Assume its HTTP  
        HttpServletRequest httpReq = (HttpServletRequest)request;
        // Check the user session for the salt cache, if none is present we create one
        @SuppressWarnings("unchecked")
        Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)
                httpReq.getSession().getAttribute("csrfPreventionSaltCache");

        System.out.println("Checking cahce befor creating it from Request :csrfPreventionSaltCache: "+csrfPreventionSaltCache);

        if(csrfPreventionSaltCache == null)
        {
            System.out.println("csrfPreventionSaltCache is null have to create new one");
            String csrfPreventionfromrequest = (String) httpReq.getSession().getAttribute("csrfPreventionSaltCache");
            System.out.println("csrfPreventionfromrequest :"+csrfPreventionfromrequest);

            // creating a new cache 
            csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000)
                    .expireAfterAccess(20, TimeUnit.MINUTES).build();

            // Setting to gttpReq
            httpReq.getSession().setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);

            System.out.println("After setting the csrfPreventionSaltCache to HttpReq");
            System.out.println("--------csrfPreventionSaltCache------ :"+httpReq.getSession().getAttribute("csrfPreventionSaltCache"));
        }



        // Generate the salt and store it in the users cache
        String salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());
        System.out.println("Salt: "+salt);
        csrfPreventionSaltCache.put(salt, Boolean.TRUE);
        // Add the salt to the current request so it can be used
        // by the page rendered in this request
        httpReq.setAttribute("csrfPreventionSalt", salt);

        System.out.println("Before going to validate salt checking for salt in request");
        System.out.println(" httpReq.getAttribute(csrfPreventionSalt) ----:"+httpReq.getAttribute("csrfPreventionSalt"));
       // System.out.println(" httpReq.getSession().getAttribute(csrfPreventionSalt) :----"+httpReq.getSession().getAttribute("csrfPreventionSalt"));


        chain.doFilter(request, response);
    }
    public void init(FilterConfig arg0) throws ServletException {

    }

    public void destroy() {

    }
}

web.xml 中的映射

        <filter>
            <filter-name>loadSalt</filter-name>
            <filter-class>com.globalss.dnb.monitor.security.LoadSalt</filter-class>
        </filter>

        <filter-mapping>
             <filter-name>loadSalt</filter-name>
             <url-pattern>/*</url-pattern>
        </filter-mapping>

为了在执行安全交易之前验证盐,我编写了另一个过滤器:

public class ValidateSalt implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        // Assume its HTTP
        HttpServletRequest httpReq = (HttpServletRequest) request;

        //String salt = (String) httpReq.getSession().getAttribute("csrfPreventionSalt");
        String salt =(String) httpReq.getAttribute("csrfPreventionSalt");
        System.out.println("I am in ValidateSalt : salt: "+salt);

     // Validate that the salt is in the cache
        @SuppressWarnings("unchecked")
        Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)
            httpReq.getSession().getAttribute("csrfPreventionSaltCache");


        if(csrfPreventionSaltCache !=null && salt !=null && csrfPreventionSaltCache.getIfPresent(salt)!=null)
        {
            // If the salt is in the cache, we move on
            chain.doFilter(request, response);
        }
        else
        {
            // Otherwise we throw an exception aborting the request flow
            throw new ServletException("Potential CSRF detected!! Inform a scary sysadmin ASAP.");
        }

    }

    public void init(FilterConfig arg0) throws ServletException {

    }
    public void destroy() {

    }

}

web.xml 中第二个 Filetr 的映射

<filter>
            <filter-name>validateSalt</filter-name>
             <filter-class>com.globalss.dnb.monitor.security.ValidateSalt</filter-class>
        </filter>

        <filter-mapping>
            <filter-name>validateSalt</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

在配置两个 servlet 之后,所有安全请求都失败了 :)。为了解决这个问题,我必须在每个以安全 URL 结尾的链接和表单帖子中添加包含同名请求参数值的 csrfPreventionSalt 参数。例如,在 JSP 页面内的 HTML 表单中:

<form action="/transferMoneyServlet" method="get">
    <input type="hidden" name="csrfPreventionSalt" value="<c:out value='${csrfPreventionSalt}'/>"/>
    ...
</form>

做完这一切后,我尝试尝试 CSRF,这就是我所做的

<html>
<body>
<form action="http://localhost:8080/mywebapp/dispatcherServlet/addUserController/addUser" method="POST" enctype="text/plain">
<input type="hidden" name="&#123;&quot;userName&quot;&#58;&quot;CSRUser&quot;&#44;&quot;password&quot;&#58;&quot;CSRFUser123&quot;&#44;&quot;roles&quot;&#58;&quot;true&quot;&#44;&quot;status&quot;&#58;&quot;true&quot;&#125;" value="" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>

当我点击提交请求按钮时,我得到了成功响应, 并且 CSRUser 已添加到我的数据库中。

我错过了什么,如何防止 CSRF 攻击?

【问题讨论】:

  • 有用!它对我有帮助

标签: java csrf-protection


【解决方案1】:

几天前我也有同样的问题,但我发现了同样的文章。

实际上,当我复制和粘贴它时,我在想它有多安全......

所以,我的第一个想法是真正了解什么是 CSRF 攻击:OWASP - Cross Site Request Forgery

了解它之后,我做的第一件事就是使用 HTTP GET 和 POST 破解我自己的应用程序,我真的很惊讶它的简单性:Here 更多关于 CSRF 的解释以及如何做到这一点)。

最后,我意识到“CSRF 攻击比表面上看到的更多”并且明确地说,这篇文章根本不保证任何安全性,因为没有关于令牌注入的解释,所以,你可以'不能保证攻击者没有持有有效的令牌。在这种情况下,过滤令牌是没有用的。

在这一点上,我建议在OWASP CSRF Prevention Cheat Sheet 中阅读更多内容以更深入地了解它。

最后但同样重要的是,就我而言,我决定使用 de OWASP CSRF Guard library,它非常可配置/灵活且开源。所以,最后,如果你决定不使用它,至少你会更好地了解如何通过遵循它的一些架构来实现它。

[更新]

不幸的是,由于以下缺点,我也不能使用 CSRF Guard:

  1. 基于同步ajax请求,正在从web平台移除:https://xhr.spec.whatwg.org/

  2. 我无法使其与动态表单生成一起使用,因为无法访问令牌以在动态表单中添加隐藏字段。我认为这是设计使然,但我无法更改我的应用以适应这一现实;

  3. 我无法使其适用于多部分表单;

幸运的是,我找到了一个很好的答案here,它指导我使用“Set-Cookie”策略进行自己的实现。

希望对你有帮助。

【讨论】:

    【解决方案2】:

    您可以使用Spring Security 进行用户身份验证和授权。从 Spring 3.2.0 版本开始默认支持Cross Site Request Forgery (CSRF)

    您还可以使用 RequestMatcher 轻松排除您不想保护的 URL:

    public class CsrfSecurityRequestMatcher implements RequestMatcher {
    private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
    private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("/unprotected", null);
    
    @Override
    public boolean matches(HttpServletRequest request) {
        if(allowedMethods.matcher(request.getMethod()).matches()){
            return false;
        }
    
        return !unprotectedMatcher.matches(request);
    }
    }
    

    Source

    【讨论】:

    • 我使用的是servlet 2.5和spring 3,spring 3.0是否支持CSRF
    • 不,Spring Security 从 3.2.0 版本开始提供 CSRF 支持。 spring.io/blog/2013/08/21/…
    猜你喜欢
    • 2014-06-02
    • 2019-05-26
    • 1970-01-01
    • 2011-02-15
    • 2015-07-08
    • 2014-08-31
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多