Protecting a RESTful JSON API from a CSRF attack

“Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated” (OWASP definition)

CSRF is related to Cross-Site Scripting (XSS), but does not require the execution of Javascript or any other front-end code. In fact, APIs are increasingly vulnerable to CSRF attacks due to the emergence of single-page web applications. A single-page web application is a Javascript “client” application that executes in the user’s browser. The client application interchanges data with the server via API calls. There are many advantages to this architecture, but it does increase the attack surface for CSRF attacks. When the user authenticates, they receive a unique token with a limited lifetime, which is usually stored as a cookie. The token is passed to the server as a header on every API call, and the server may return a new token with an updated expiration time with every response.

Testing an API Endpoint for Vulnerability to CSRF

A CSRF attack takes place when a targeted user is logged into a web application and possesses a valid authentication token. The user is then tricked into opening a malicious web page in another browser and performing an action that triggers an HTML form submission. The form submission attempts to interact with the site that the user is logged into. The user’s browser automatically submits the cookie containing the valid authentication token, allowing the request to proceed as if it were an intentional action by the user. Here’s an example of a malicious page that’s designed to target administrative users of a secure web site:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Malicious Page</title>
  </head>
  <body>

<h1>Alert! You have used up 90% of your Internet data allowance this month!</h1>
<form action="https://secure.example.com/user" method="POST" enctype="text/plain">
        <input type="hidden" name='{"firstName":"first","lastName":"last","email":"somebody@gmail.com","role":"admin","birthDate":"1995-08-18","ignore":"' value='test"}'/>
        <input type="submit" value="Dismiss"/>
    </form>

  </body>
</html>

If the user sees this page and clicks “Dismiss” while logged into the secure site, an authenticated POST request will come from the user’s browser and create a new admin account for the attacker. This malicious page is obviously suspicious, but you can see how someone could craft a page that looks very similar to a valid site that the targeted user interacts with on a daily basis.

Hardening an API Against CSRF Attacks

This particular attack can be blocked in two ways:

  1. Restrict all API requests to content-type application/json. If it’s a JSON API, then all requests should come in with that content type, and there is no downside to blocking content-type text/plain and application/x-www-form-urlencoded
  2. Require that the access token be present in the header of the request, and ignore tokens passed in as cookies. The Javascript single-page application should be built to use the cookies only for storage (if at all), and pass the authentication token to the API in a header. If the header isn’t present, the API should reject the request.

I do not claim to be a security expert or penetration tester, so there other mitigation techniques might be required. Check out the OWASP Cross-Site Request Forgery Prevention Cheat Sheet for a more complete description.

References

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.