Web
- Security - CSRF
- [Security - CORS]
- [Security - XSS]
- [Security - WAF]
- API Versioning
Security - CSRF
CSRF is a common attack during which a victim user is exploited by attacker tricking user into using authenticated credentials (such as session cookie) to perform unintended actions, i.e. transfer fund from user’s bank to that of attacker.
The predominant solution to protect against CSRF attack is using a CSRF token. And this CSRF token is to be included in every stateful request. It works as follows:
- Server includes a randomly generated CSRF token in client’s frontend presentation, such as
<meta>tag. - Server stores such CSRF token in HTTP session that is stored in session storage, i.e. Redis
- Client sends a request along with CSRF token in the form of HTTP parameter or header.
- Server receives the client request and verifies CSRF token with what’s in the session storage.
- Server rejects if token mismatches (Maclious request where attacker fails to forge the correct CSRF token).
Tech stack:
- Spring Security
- Spring Session
- Html + React/VueJs
Example HTML:
<meta name="_csrf" content="${_csrf.token}"/>
Example http request using javascript:
let csrf_token = document.querySelector('meta[name="_csrf"]').getAttribute('content');
let headers =
{
headers : {
"X-CSRF-TOKEN" : csrf_token,
"Content-Type" : "application/json"
}
};
axios
.post("/api/transfer", data, headers)
.then(response => response.data)
.then(data => // ...
})
.catch(error => //...);
We can define in our Boot application:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()...
}
By default, CSRF protection is enabled by Spring Security so that above is redundant. Only required part is to provide the backing session storage if we opt for storing CSRF in HTTP Session, which is recommended over Session Cookie. Like so:
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
And that’s it!
API Versioning
API versioning is debatable. One would argue that a good API design would not subject itself to versioning. Nonetheless, there are several approaches in designing versioning in API. In HTTP, these are the common ones:
- URL path (i.e.
/api/v1/users) - Query Parameters (i.e.
/api/users?version=1.2.3) - HTTP headers (Content Negotiation)
Let’s look at versioning using MIME type:
@RequestMapping(produces = "application/my.api.v1+json")
public class MyAPIV1Controller {}
@RequestMapping(produces = "application/my.api.v2+json")
public class MyAPIV2Controller {}
This way we will not introduce breaking changes whilst clients migrate to newer version.