-
Notifications
You must be signed in to change notification settings - Fork 59
Basic CSRF Attack Simulation & Protection
Source Folder: csrf-protection
Tech Stack:
- spring-boot
- spring-mvc
- spring-security
- thymeleaf
- 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?
- You have an application with endpoints which you want to prevent unauthorized access. Therefore, you have secured your resources with server-side session management.
- You have a page including a form where your users submit data which results in the execution of a task
- 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
- A user of your system visits your app and logs in.
- The same user visits the application of the attacker and clicks the tricky button without being aware of what it actually does.
- Creating an application with Spring Boot - The actual application
- Protect the resource against unauthorized access with Spring Security
- Disable the CSRF protection of Spring Security (because it is enabled by default)
- Create another application with Spring Boot - The attacker's application
- Simulate a CSRF attack
- 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
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.
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);
}
}
- Run the actual app
- Try to go to the protected resource. The browser will ask you for username and password. Go ahead and login to the system
- Run the attacker app
- Open the attacker app's homepage.
- 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.
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:
- Delete the line that disables csrf in the SecurityConfig class of the actual application.
- 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.
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.
- Home
- JPA Specification
- JMS with Spring Boot
- JMS with Spring Boot & Apache Camel
- Caching with Spring Boot & Redis
- Netflix Eureka with Spring Boot
- Netflix Hystrix & Eureka with Redis Cache
- Netflix Zuul
- Authentication Types
- Separating Unit & Integration Tests Execution Phases with Maven
- Basic CSRF Attack Simulation & Protection
- Spring Batch
- Dagger
- Concurrency with Java
- Swagger2 with Spring Boot