如何防止我的 Web 应用程序在 Java 中受到 CSRF(跨站点请求伪造)的影响

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37726903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:37:20  来源:igfitidea点击:

How to prevent my web app from CSRF(Cross site request forgery) in java

javacsrf-protection

提问by Varun

I am trying to prevent my web application from CSRF(Cross site request forgery) I followed this link Link for CSRF

我试图阻止我的 Web 应用程序从 CSRF(跨站点请求伪造)我按照这个链接链接 CSRF

This is what I have tried. To implement this mechanism in Java I choose to use two filters, one to create the salt for each request, and another to validate it. Since the users request and subsequent POST or GETs that should be validated do not necessarily get executed in order, I decided to use a time based cache to store a list of valid salt strings.

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

The first filter, used to generate a new salt for a request and store it in the cache can be coded as follows:

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

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() {

    }
}

Mapping in web.xml

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>

to validate the salt before executing secure transactions I have written another filter:

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

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() {

    }

}

Mapping for the second Filetr in web.xml

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>

After configuring both servlets all your secured requests wasfailing :). To fix it you I had to add, to each link and form post that ends in a secure URL, the csrfPreventionSalt parameter containing the value of the request parameter with the same name. For example, in an HTML form within a JSP page:

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

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

After doing all this I try to attempt CSRF, This what I did

完成所有这些之后,我尝试尝试 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>

When I hit the submit request button , I got a success response, and CSRUser was added in my database.

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

am I missing something, how do I prevent CSRF attack?

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

回答by Seo Bro

You can use Spring Securityfor users authentication and authorization. There is Cross Site Request Forgery (CSRF)support by default starting from Spring 3.2.0 version.

您可以使用Spring Security进行用户身份验证和授权。有跨站请求伪造(CSRF)默认支持春季3.2.0版开始。

You can also easily exclude the URLs you do not want to protect by using RequestMatcher:

您还可以使用 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

来源

回答by Reginaldo Santos

I had the very same questions some days ago and I've found the very same article.

几天前我有同样的问题,我找到了同样的文章。

Actually, while I was copying and pasting it I was thinking about how secure it was ...

实际上,当我复制和粘贴它时,我正在考虑它的安全性......

So, my first idea was to really get in a quest to understand what CSRF attacks were: OWASP - Cross Site Request Forgery.

所以,我的第一个想法是真正了解什么是 CSRF 攻击:OWASP - 跨站点请求伪造

After understand it, first thing I did was to hack my own app with both HTTP GET and POST and I was really surprise about how easy it was: Heresome more explanation about CSRF and how to do it).

理解之后,我做的第一件事就是用 HTTP GET 和 POST 来破解我自己的应用程序,我真的很惊讶它是多么容易:这里有一些关于 CSRF 的更多解释以及如何做)。

Finally, I realize "there is more in CSRF attacks than meets the eye" and definetly, this article does not guarantee any security at all since there's no explanation about token injection and so, you can't assure the attacker is not in possession of a valid token. In this case, filtering token is useless.

最后,我意识到“ CSRF 攻击中的东西比眼睛看到的要多”,并且明确地说,本文根本不保证任何安全性,因为没有关于令牌注入的解释,因此,您不能保证攻击者不拥有一个有效的令牌。在这种情况下,过滤令牌是没有用的。

In this point, I recomend some more reading in OWASP CSRF Prevention Cheat Sheetto get more deep into it.

在这一点上,我建议阅读更多OWASP CSRF 预防备忘单以更深入地了解它。

Last but not least, in my case, I've decided to use de OWASP CSRF Guard librarywhich is very configurable/flexible and open source. So, in the end, if you decide not to go with it, at least you will have a better idea of how to implement it your own by following some of its architecture.

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

[update]

[更新]

Well, unfortunally I couldn't go with CSRF Guard either, because of the following drawbacks:

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

  1. It is based on synchronous ajax request which is in the process of being removed from the web platform: https://xhr.spec.whatwg.org/

  2. I couldn't make it work with dynamic form generation because it is not possible to access the token to add a hidden field in the dynamic form. Which is by design I think, but I could not change my app to adapt to this reality;

  3. I couldn't make it work with multipart forms;

  1. 它基于同步 ajax 请求,正在从 Web 平台中删除:https: //xhr.spec.whatwg.org/

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

  3. 我无法让它与多部分表单一起工作;

Fortunately, I've crossed a very good answer herewhich directs me to do my own implementation using "Set-Cookie" strategy.

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

I hope it helps.

我希望它有帮助。