CSRF stands for Cross Site Request Forgery.

This is a very common Web attack, which is very defensible. However, it is often ignored by many developers, so many websites are actually vulnerable to CSRF attacks.

Today, Songo will talk to you about what a CSRF attack is and how to defend against it.

This is the 18th article in this series. Reading the previous articles in this series will help you understand this article better:

  1. Dig a big hole and Spring Security will do it!
  2. How to decrypt the password
  3. A step-by-step guide to customizing form logins in Spring Security
  4. Spring Security does front and back separation, so don’t do page jumps! All JSON interactions
  5. Authorization in Spring Security used to be so simple
  6. How does Spring Security store user data into the database?
  7. Spring Security+Spring Data Jpa, Security management is only easier!
  8. Spring Boot + Spring Security enables automatic login
  9. Spring Boot automatic login. How to control security risks?
  10. How is Spring Security better than Shiro in microservices projects?
  11. Two ways for SpringSecurity to customize authentication logic (advanced play)
  12. How can I quickly view information such as the IP address of the login user in Spring Security?
  13. Spring Security automatically kicks out the previous login user.
  14. How can I kick out a user who has logged in to Spring Boot + Vue?
  15. Spring Security comes with a firewall! You have no idea how secure your system is!
  16. What is a session fixed attack? How do I defend against session fixation attacks in Spring Boot?
  17. How does Spring Security handle session sharing in a clustered deployment?

1. CSRF principle

If you want to defend against CSRF attacks, you have to first understand what a CSRF attack is. Songo combs the flow of CSRF attacks with the following figure:

The process is simple:

  1. Assume that the user opened China Merchants bank online banking website, and login.
  2. After a successful login, the online bank returns a Cookie to the front end, and the browser saves the Cookie.
  3. A user opens a new TAB in his browser and visits a dangerous site without logging out of online banking.
  4. The dangerous website had a hyperlink to the address of China Merchants Bank online bank.
  5. The user clicks on the hyperlink, and since the hyperlink automatically carries a Cookie saved in the browser, the user unknowingly visits the online bank, potentially causing himself a loss.

The process of CSRF is basically like this. Next, Songo uses a simple example to show how CSRF works.

2. CSRF practice

Next, I create a Spring Boot project named CSRF-1. This project is equivalent to the online banking website we mentioned above, and introduces the Web and Spring Security dependencies to create the project as follows:

Once created, for convenience, we directly configure the Spring Security username/password in the application.properties file:

spring.security.user.name=javaboy
spring.security.user.password=123
Copy the code

Then we provide two test interfaces:

@RestController
public class HelloController {
    @PostMapping("/transfer")
    public void transferMoney(String name, Integer money) {
        System.out.println("name = " + name);
        System.out.println("money = " + money);
    }
    @GetMapping("/hello")
    public String hello(a) {
        return "hello"; }}Copy the code

Assume/Transfer is a transfer interface (this is an assumption, mainly to demonstrate the CSRF attack, real transfer interface is more complex).

Finally, we need to configure Spring Security, because Spring Security automatically protects against CSRF attacks by default, so we need to turn this off:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .and() .csrf() .disable(); }}Copy the code

After the configuration is complete, we start the CSRF-1 project.

Next, we will create a CSRF-2 project, which is equivalent to a dangerous website, and for convenience we will only need to introduce web dependencies.

After the project is successfully created, first modify the project port:

server.port=8081
Copy the code

Then we create a hello.html in the resources/static directory that looks like this:

<body>
<form action="http://localhost:8080/transfer" method="post">
    <input type="hidden" value="javaboy" name="name">
    <input type="hidden" value="10000" name="money">
    <input type="submit" value="Click to view pictures of beautiful women">
</form>
</body>
Copy the code

There is a hyperlink, the hyperlink text is click view your beautiful pictures, when you click on a hyperlink, automatically request http://localhost:8080/transfer interface, hidden field also carry the two parameters at the same time.

After the configuration is complete, you can start the CSRF-2 project.

Next, the user first visit CSRF – 1 the project interface, at the time of access to login, the user is performing the login operation, following a visit to complete, the user does not perform logout operation, then the user to access a CSRF – 2 in the page, see the hyperlinks, curious about the beauty what looks like, one click, the money is being transferred.

3. CSRF defense

Let’s start with the defense idea.

One of the core ideas of CSRF defense is to add a random number to the front end request.

Because in the CSRF attack, the hacker website actually does not know what the user’s Cookie is, he asks the user to send the request to the online banking website, because the process will automatically carry the information in the Cookie.

Therefore, our defense idea is as follows: when users visit online banking, in addition to carrying the information in the Cookie, they also need to carry a random number. If the user does not carry this random number, the online banking website will reject the request. When hacker websites induce users to click on hyperlinks, they will automatically carry the information in cookies, but will not automatically carry random numbers, thus successfully avoiding CSRF attacks.

Spring Security provides good support for this, so let’s take a look.

3.1 Default Scheme

Spring Security actually provides CSRF defense by default, but requires a lot of work from the developer.

Let’s start by creating a new Spring Boot project with Spring Security, Thymeleaf, and Web dependencies.

After the project is successfully created, we still configure the username/password in application.properties:

spring.security.user.name=javaboy
spring.security.user.password=123
Copy the code

Next, we provide a test interface:

@Controller
public class HelloController {
    @PostMapping("/hello")
    @ResponseBody
    public String hello(a) {
        return "hello"; }}Copy the code

Note that this test interface is a POST request, because by default, GET, HEAD, TRACE, and OPTIONS are not required to validate CSRF attacks.

Then go to the Resources/Templates directory and create a new Thymeleaf template as follows:

<body>
<form action="/hello" method="post">
    <input type="hidden" th:value="${_csrf.token}" th:name="${_csrf.parameterName}">
    <input type="submit" value="hello">
</form>
</body>
Copy the code

ParameterName = ${_csrF.token}; ${_csrF.token}; ${_csrF.token};

These two values are automatically brought in by the server and we just need to render them in the front end.

Add a controller to the front-end hello.html page as follows:

@GetMapping("/hello")
public String hello2(a) {
    return "hello";
}
Copy the code

After the login is successful, we can see that there is an additional parameter in the login request, as follows:

As you can see, there is also an _cSRf parameter.

Here we use Spring Security’s default login page. If you want to use a custom login page, you can refer to hello. HTML above and pass _csrf through a hidden field.

After visiting the Hello page, click the button to access the Hello interface.

You can remove the _csrf parameter from the hello. HTML page to see the effect of accessing the Hello interface.

This is the default scenario in Spring Security, bringing relevant data to the front end through the Model.

If you are working on a project where the front and back ends are separated, this will be fine. If you are working on a project where the front and back ends are separated, this is obviously not enough.

3.2 Solution for Separating front and rear ends

Spring Security also provides a solution for back-end separation projects.

Instead of putting _cSRf in Model to return to the front end, put _cSRF in Cookie to return to the front end as follows:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); }}Copy the code

Some friends may say that the Cookie is not stolen by hacker websites? In fact, it won’t. Please pay attention to the following two questions:

  1. The hacker site doesn’t know what’s inside your Cookie, and he doesn’t need to, because CSRF attacks automatically carry the Cookie’s data with them.
  2. We put the random number generated by the server in the Cookie, and the front end needs to extract it from the Cookie by itself_csrfParameters, and then concatenated into parameters passed to the back end, it is useless to simply send the data in the Cookie to the server.

Fully understand the above two points, you’ll find _csrf on Cookie is no problem, but attention, configuration we obtained through withHttpOnlyFalse CookieCsrfTokenRepository instance, This method sets the HttpOnly attribute in the Cookie to false, which allows the front end to manipulate the Cookie through JS (otherwise you can’t get _csrf).

After the configuration is complete, restart the project. At this point, we find that there is an extra item in the Cookie returned:

Next, let’s take a look at how the front end works by customizing the login page.

First we create a new HTML page called login.html under the resources/static directory:


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery.min.js"></script>
    <script src="js/jquery.cookie.js"></script>
</head>
<body>
<div>
    <input type="text" id="username">
    <input type="password" id="password">
    <input type="button" value="Login" id="loginBtn">
</div>
<script>
    $("#loginBtn").click(function () {
        let _csrf = $.cookie('XSRF-TOKEN');
        $.post('/login.html', {username: $("#username").val(),password: $("#password").val(),_csrf:_csrf},function (data) { alert(data); })})</script>
</body>
</html>
Copy the code

Let me explain this HTML to you:

  1. Jquery and jquery.cookie are introduced first, so that we can manipulate cookies in a moment.
  2. Define three inputs, the first two are the username and password, and the third is the login button.
  3. After clicking the login button, we first extract the XSRF-Token from the Cookie, which is the CSRF parameter we upload.
  4. Perform the login operation with a POST request, bearing in mind that_csrfParameters.

On the server side, we also changed it slightly as follows:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .successHandler((req,resp,authentication)->{
                    resp.getWriter().write("success"); }) .permitAll() .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); }}Copy the code

On the one hand, this is releasing the JS file.

On the other hand, let’s configure the login page and the successful login callback. In this case, I’ll just give a string for the successful login callback. If you’re interested, check out the previous article in this series for a detailed explanation of successful login callbacks.

OK, after everything is done, we go to the login. HTML page and enter the username and password to login. The result is as follows:

As you can see, our _cSRF configuration has taken effect.

Try removing _cSRf from the login parameter and see what happens.

4. Summary

Well, today I mainly introduce CSRF attack and how to defend against it. As you can see, CSRF attacks mainly use the browser default Cookie sending mechanism, so if your front end is App, applets and other applications, not involving browser applications, in fact, you can ignore this problem, if your front end contains browser applications, this problem should be seriously considered.

Well, that’s all for this article. Related cases in this article have been uploaded to GitHub, and you can download them by yourself :github.com/lenve/sprin…

Ok, do you guys GET it? If you have a harvest, remember to point under the encouragement of songge oh ~