Principle and defense of CSRF vulnerability

Posted Jun 26, 20205 min read

image

CSRF Full name:Cross Site Request Forgery, translated:Cross-site request forgery

Scenes

After clicking a link, I discovered that the account was stolen, the money was diverted, or some inexplicable comments, etc., were performed without my knowledge.

What is CSRF

csrf is a script that can send http requests. The victim can be disguised to send a request to the website to achieve the purpose of modifying the website data.

Principle

When you log in to a website on your browser, the cookie will save the login information, so that you don t have to log in every time you continue to visit, everyone knows this. CSRF uses this login state to send malicious requests to the backend.

Why can the script get the cookie of the target website?
As long as the target website is requested, the browser will automatically bring the cookie under the domain name of the website. Looking at the script below, it can be proved that the malicious script can obtain the login information of the CSDN website.
The premise is that you have logged into the CSND website on your browser.

<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>csrf demo</title>
    </head>
    <body>
    Your on CSDN
        Fans:<span id="fans_num"></span>
        Follows:<span id="follow_num"></span>
        <script>
            fetch('https://me.csdn.net/api/relation/get', {
              credentials:'include'
            }).then(res => res.json())
            .then(
                res => {
                document.getElementById('fans_num').innerText = res.data.fans_num;
                document.getElementById('follow_num').innerText = res.data.follow_num;
            })
        </script>
    </body>
</html>

To ensure the login status of CSDN, open this html file with a browser, you can see that this script has obtained my user information on csdn. And the number of shabby fans!
F12 opens the Cookie on the left side of the application selection column and some information about the current user from the csdn website.
image.png
image.png
image.png

This script allows each different logged-in user to open, and will display the number of followers and fans according to the current user.
This is enough to show that you can get information about the current user of the target website and can send requests on behalf of the user.
This is just a harmless get request, what if it is a post request?

CSRF attack

Knowing the principle, the attack becomes easy to understand. Following the example above,
I changed the request address to the url to comment on this article, the parameter is "This article is written 6",
In the absence of CSRF defense, I post a comment such as:Remove secrets:, followed by a link to this script, as long as a user clicks the link, he will post a comment on this article in his name " This article is written 6".

CSDN must have done defense, I will not waste my effort.

CSRF defense

Three defense methods:

1. SameSit

Third-party websites are prohibited from using cookies from this site.

This is when the backend sets the value of SameSite to Strict or Lax when setting the cookie.
When setting Strict, all requests on behalf of third-party websites cannot use cookies from this site.
When setting Lax, it means that only the third party website's GET form, <a> tag and <link> tag can carry cookies.
When None is set, it means the same as not set.

@Bean
public CookieSerializer httpSessionIdResolver(){
    DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
    cookieSerializer.setCookieName("JESSIONID");
    cookieSerializer.setUseHttpOnlyCookie(true);
    cookieSerializer.setSameSite("Lax");
    cookieSerializer.setUseSecureCookie(true);
    return cookieSerializer;
}

Disadvantages:
Currently only supported by chrome browser...

2. referer

referer represents the source of the request and cannot be forged.
The backend writes a filter to check the referer in the headers of the request, and checks whether it is a request from this website.
Off topic:
The difference between referer and origin is that only the post request will carry the origin request header, and the referer will carry it in any case.
The correct spelling of referer should be referrer. HTTP standard makers will make mistakes and do not intend to change them.
Disadvantages:
The browser can turn off referer..........

3. token

The most common defense method is that the backend generates a token and puts it in the session and sends it to the frontend. The frontend carries this token when sending a request. The backend determines whether it is a request of this website by checking whether the token and the token in the session are consistent. .

Implementation:
The user logs in, enters the account password, requests the login interface, and the backend puts the token in the session when the user login information is correct, and returns the token to the frontend. The frontend stores the token in the localstory, and then sends the request to put the token in header.
Write a filter on the backend to intercept POST requests. Pay attention to ignore requests that do not require tokens, such as logging in to the interface to obtain the token, so as not to check the token before obtaining the token.
Verification principle:Posts that have the same token in the session and in the front-end header are released.

/**
 * @author mashu
 * Date 2020/6/22 9:37
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "verificationTokenFilter", description = "Used to verify tokens")
public class VerificationTokenFilter implements Filter {

    List<String> ignorePathList = ImmutableList.of("/demo/login","/demo/getToken");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest =(HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse =(HttpServletResponse) servletResponse;
        //Ignore requests that do not require tokens
        String serviceUrl = httpServletRequest.getServletPath();
        for(final String ignorePath:ignorePathList) {
            if(serviceUrl.contains(ignorePath)) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
        }
        String method = httpServletRequest.getMethod();
        if("POST".equals(method)) {
           String tokenSession =(String)httpServletRequest.getSession().getAttribute("token");
           String token = httpServletRequest.getHeader("token");
            if(null != token && null != tokenSession && tokenSession.equals(token)) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            } else {
                log.error("Failed to verify the token!" + tokenSession + "!=" + token);
                httpServletResponse.sendError(403);
                return;
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}