A Cross-Site Request Forgery (CSRF) is a common malicious attack because it requires little technical expertise. The combination of the ease of execution, low barriers for executing it, and the prevalence of targets require active measures against it.
Let’s start with a few definitions.
Cross-Site Request Forgery
As explained by OWASP, a CSRF, is a popular attack vector on a website or SaaS application. It’s a type of malicious exploitation of a website where unauthorized commands are submitted from a user that the web application trusts. So the key ingredients are:
A website (the target)
A trusted, legitimate user
Malicious code that tricks the website into carrying out an unintended action
If the victim is a normal user, the malicious code can make the user transfer funds, send emails, load images, and so on. If the victim is the website’s administrator, the entire system can be compromised.
Why Is It Important to Mitigate CSRF?
As stated above, CSRF requests are easy to execute, can have dire consequences, and have a broad attack surface. They are serious attacks – something that every website and SaaS application needs to take into account. Transferring funds to an illicit bank account is catastrophic for a financial institution. But even if we are talking about a social media app, posting NSFW content on a user’s wall or other personal space can have serious consequences as well.
Spring Framework
The Spring Framework is a popular Java framework for developing enterprise and web applications. Originally created in 2005 by Pivotal, it is still going strong today. Over the years, different modules have been added to the core Spring container. One of the most popular is Spring Boot. The Convention over Configuration (CoC) is part of the Spring framework. It allows you to code with minimal configuration.
Spring Security
Another popular module is Spring Security. As the official documentation explains very well, “Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications. Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.” In other words, this is the standard security module for Spring-based applications. It provides protection against attacks like session fixation, clickjacking, and cross-site request forgery.
A CSRF attack tricks a system into executing actions that it thinks were initiated by a legitimate user
Spring CSRF in Java
Spring is written in Java, so we need to discuss mitigating CSRF in Java first. In some cases, preventing a Java CSRF or even a general CSRF is the same as preventing a Spring CSRF. As stated above, a CSRF attack tricks a system into executing actions that it thinks were initiated by a legitimate user. There are three ways to handle it:
The wrong way (which allows an attacker to execute a CSRF)
The right way in Java (preventing any CSRF attempts)
The right way with Spring Security
Example
Let’s look at the classic scenario that involves a user sending funds to a bank account.
The Wrong Way
GET http://somebank.com/transfer.funds?account=validAccount&amount=1000 HTTP/1.1
What we have here is a basic HTTP request with all the relevant data in the GET
parameters: the amount to transfer, the destination account, etc. The fact that this is an HTTP request allows every other user on the local network to eavesdrop on the request and create a malicious version:
GET http://somebank.com/transfer.funds?account=malicousAcconut&amount=100000 HTTP/1.1
Thus, the hacker transfers funds to a different bank account than the user intended. The attacker is exploiting the fact that this is a plain HTTP request with all the business logic available in the GET parameters.
The Right Way
First and foremost, this should be an HTTPS request. Although HTTPS doesn’t do anything directly to prevent a CSRF attack, it’s a prerequisite to providing at least a minimal amount of security to a financial transaction. HTTPS makes the data exchanged between the browser and the website encrypted, thus making the life of an attacker harder. In addition, switching from a GET
request to a POST
request helps, but it doesn’t fully protect you from a CSRF because the same attack can be rendered via a simple form. For instance, we can send the data in a POST request this way:
POST http://somebank.com/transfer.funds HTTP/1.1 account=validAccount&amount=1000
It’s easy to create an HTML form that will be presented to the legitimate user and allow the attacker to yet again execute his attack:
<form action="http://somebank.com/transfer.funds" method="POST">
<input type="hidden" name="account" value="malicousAccount"/>
<input type="hidden" name="amount" value="1000000"/>
<input type="submit" value="transfer funds"/>
</form>
A proper way to do this in Java can be via a web filter that makes sure that the HTTP/s origin of the request is what we expect it to be and that it matches the server host. This way, if the request comes from a different source, we can block it:
public void Filter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse) response;
String source = httpReq.getHeader("Origin");
URL sourceURL = new URL(source);
if (!this.targetOrigin.getHost().equals(sourceURL.getHost())) {
httpResp.sendError(HttpServletResponse.SC_FORBIDDEN, "hosts don't match");
return;
}
}
This combined with HTTPS and at least one additional mitigation technique (like short-lived session-only cookies) provides adequate protection against CSRF attacks.
The Spring Security Way
There’s another way to do this. Spring Security uses the “Synchronizer Token Pattern,” or STP. This means that we embed a unique value in each request and session from the browser and validate this token every time on the server. For every request or session, we expect to see a certain value from the browser. And if we don’t see it, we block the request. The value is generated by the server and put into the source code of the HTML page (or HTTP header), and the server expects to see it (or a version of it) in the next request. So for the example above, here is what the form should look like:
<form action="http://somebank.com/transfer.funds" method="POST">
<input type="hidden" name="account" value="malicousAccount"/>
<input type="hidden" name="amount" value="1000000"/>
<input type="submit" value="transfer funds"/><input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
Without this token, the attacker won’t be able to execute an attack. To generate this token with Spring Security, we don’t have to do much as this functionality is built in and enabled by default. It can be disabled by adding this code:
@Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable(); }
So we need to make sure that is not in our code.
Conclusion
CSRF is a common and high-risk attack vector. However, as we’ve seen above, it’s fairly easy to mitigate this risk with HTTPS, origin verification, and STP. As long as you’re following the best practices, CSRF attacks will be blocked. Thankfully, in Spring we have CSRF protection built in, so all we have to do is make sure that we don’t disable it.
This post was written by Alexander Fridman. Alexander is a veteran in the software industry with over 11 years of experience. He worked his way up the corporate ladder and has held the positions of Senior Software Developer, Team Leader, Software Architect, and CTO. Alexander is experienced in frontend development and DevOps, but he specializes in backend development.