Skip to content

Basic CSRF Attack Simulation & Protection

Furkan Kürşat Danışmaz edited this page Oct 16, 2018 · 6 revisions

Source Folder: csrf-protection

Tech Stack:

  • spring-boot
  • spring-mvc
  • spring-security
  • thymeleaf

Problems Solved

  • CSRF Protection with Spring Security

Suppose that you have an application with protected resources, meaning that you want to prevent unauthorized access to it. So, you have decided to secure your resources to prevent unauthorize access but still, you're vulnerable to CSRF attacks. How?

A Sample Scenario

  1. You have an application with endpoints which you want to prevent unauthorized access. Therefore, you have secured your resources with server-side session management.
  2. You have a page including a form where your users submit data which results in the execution of a task
  3. There is an application prepared by an attacker in which he/she hides a similar form and a tricky button which submits it with default values in it
  4. A user of your system visits your app and logs in.
  5. The same user visits the application of the attacker and clicks the tricky button without being aware of what it actually does.

Outline

  1. Creating an application with Spring Boot - The actual application
  2. Protect the resource against unauthorized access with Spring Security
  3. Disable the CSRF protection of Spring Security (because it is enabled by default)
  4. Create another application with Spring Boot - The attacker's application
  5. Simulate a CSRF attack
  6. Enable the CSRF protection back and see the result.

Before enabling the CSRF protection, the attacker will succeed to make requests to your application because:

  • Your actual application will think that your user being attacked sent request intentionally
  • Your actual application will not prompt username and password since the user being attacked is actually authenticated

The Actual App

Dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>        
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
</dependencies>

Security Config

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public AuthenticationProvider authenticationProvider() {
        return new SampleAuthenticationProvider();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(this.authenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic();

        http.csrf().disable(); // For demonstrating purposes
    }
}

Sample authentication provider

public class SampleAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        if (this.validateCredentials(username, password)) {
            return new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>());
        }

        return null;
    }

    private boolean validateCredentials(String username, String password) {
        return username.equals("johndoe") && password.equals("password");
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

Protected Resource:

@Controller
@RequestMapping("/account")
public class AccountController {

    @GetMapping
    public String transferMoney() {
        return "account";
    }

    @PostMapping("/transfer")
    public String transfer() {
        return "success";
    }
}

The entry point:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}

The account.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
    <meta charset="UTF-8">
    <title>Actual Application</title>
</head>
<body>

    <form action="/account/transfer" method="post">
        <input type="submit" value="Transfer Money">
    </form>

</body>
</html>

By default, csrf protection is enabled in Spring Security but we've closed it for demonstration purposes.

The Attacker's App

Dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Controller rendering the tricky page

@Controller
@RequestMapping("/home")
public class HomeController {

    @GetMapping("/index")
    public String index() {
        return "index";
    }
}

The Tricky Page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Attacker Application</title>
</head>
<body>
    <form action="http://localhost:9090/account/transfer" method="post">
        <input type="submit" value="Tricky Button">
    </form>
</body>
</html>

The entry point:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class);
    }
}

Test Steps

  1. Run the actual app
  2. Try to go to the protected resource. The browser will ask you for username and password. Go ahead and login to the system
  3. Run the attacker app
  4. Open the attacker app's homepage.
  5. Click on the Tricky button

You'll see that the attacker app will successfully make a POST request to the protected resource in your actual app.

How To Prevent CSRF Attacks

As I said before, Spring Security has built-in protection for CSRF attacks. So you need not do anything for your form posts. However, you need to add CSRF header before sending any AJAX request.

For this example:

  1. Delete the line that disables csrf in the SecurityConfig class of the actual application.
  2. Change the account.html file in the actual application from:
<form action="/account/transfer" method="post">
    <input type="submit" value="Transfer Money">
</form>

to

<form th:action="@{/account/transfer}" method="post">
    <input type="submit" value="Transfer Money">
</form>

Now, restart your applications and execute the same test scenario again. You will be able to use your actual application successfully but this time you will get an error when you click on the Tricky button.

But, what is the difference on the webpage?

Open your actual application and inspect the account.html in the browser. You should see a hidden element like below inside your <form> element:

<input type="hidden" name="_csrf" value="c621d314-e95f-4955-b4cb-355a00bc4464">

Definitely, we didn't put any HTML code like this into our account.html. This is done during the server side rendering operation. There is no way, that the attacker app can know this UUID and can make a successful request to the actual application. Therefore, you are now protected against CSRF attacks.

Clone this wiki locally