Is your Web API susceptible to a CSRF exploit?

Cross-site request forgery (CSRF) is a type of security exploit where a user’s web browser is tricked by a third-party site into performing actions on websites that the user is logged into. It is often a difficult attack to pull off, as it requires a number of factors to line up at once. Protecting against it requires good discipline and good design practices, especially when it comes to protecting Web APIs.

Here’s a brief example of a fictitious CSRF attack against a bank:

  1. Sally logs into her bank, bank.example.com, with her account number and secure password.
  2. The bank sends a cookie to her web browser, identifying her “session”, which is the period for which she’s logged in to her account. Set-Cookie: SESSIONID=a804696f-93fc-48cf-9b02-267d9ed773c0
  3. Some time later, Sally is browsing other web sites not related to her bank. A search lands her on hungryhippos.ca.
  4. hungryhippos.ca hosts some malicious code in their web page:
    <form name="attack" enctype="text/plain"
        action="https://bank.example.com/api/transfer" METHOD="POST">
      <input type="hidden" name='{"from": "Savings", "to": "00302319550440", "amount": "100.00"}'>
    </form>
    <script>document.attack.submit();</script>
  5. Sally’s web browser reads the web page, and executes the code on the page, which includes sending a request to her bank to transfer $100.00 out of her savings account.
  6. Sally’s browser also includes her SESSIONID cookie with the request, because it is a request to bank.example.com.
  7. Sally’s bank sees an authorized request to transfer money come in, with a valid session cookie, from the same IP address that the session logged-in as. The transfer is authorized.

So, how do we protect against requests that come from our users, but aren’t actually performed by them?

Traditional UI Protection

In a traditional web application, where the user primarily interacts with the application server by performing link-based navigation and form-based modification actions, you’d protect against a CSRF attack by one or more of these approaches:

  • Check the Referer header of the HTTP request to ensure that requests are generated from your own site. Don’t stop reading! I know that this sounds stupid because Referer headers are easily spoofed, right? But a CSRF attack relies on tricking a user’s browser into performing actions, so it is not possible to spoof the Referer header, because you don’t control the HTTP client. (Edit: If you disagree with me, please, tell me how such an attack would work! I want to know!)
  • Embed unguessable user-specific data in every HTTP request that performs modification actions; primarily this would be HTML forms that perform POSTs. This protects against the CSRF attack because the malicious site (eg. hungryhippos.ca) doesn’t know the correct user-specific data to put into the POST payload. The backend server must validate that the user-specific data is correct for every POST.
    • The simplest choice for user-specific data is the user’s session ID. That is easy to validate on the server-side.
    • Bonus points: Another option is a version identifier or last-modified timestamp of the object being edited. Checking this server-side can also serve the purpose of preventing two edits of a resource at the same time from overwriting each other. Note that a version identifier would have to be unguessable for this to work, so an auto-incrementing number is not a good candidate.

Web API Protection

Protecting a Web API requires a slightly different approach, because the API won’t have an opportunity to tell it’s client, “here, send this data as well” for each request. Here are some mechanisms for protecting a Web API from a CSRF attack:

  • Don’t allow your API to be accessed with the same credentials that your interactive UI sessions use. In the example above, if the bank’s API didn’t accept a SESSIONID cookie as a valid credential, the transfer request would not have been possible.
    • Do: use an API-key based authentication, or a more sophisticated mechanism like OAuth.
    • Don’t: allow HTTP basic, digest, or NTLM authentication to your API. If Sally’s bank hadn’t accepted SESSIONID, and instead presented WWW-Authenticate: Basic realm="bank.example.com" in its response, Sally would’ve been prompted to enter her bank account credentials. There’s a chance any user might enter whatever is being asked for.
    • Except: HTTP authentication mechanisms are totally safe if they’re just being used to transport an API key.
  • Requiring user-specific data can also be used to mitigate API CSRF attacks, but it doesn’t work the same way as an HTML form. Instead, imagine if Sally’s bank’s API had required that the source bank account be specified as an account number, rather than an account name. The attackers at hungryhippos.ca wouldn’t have been able to supply Sally’s bank account number for the transfer.
    • Partial fix:This form of protection is less effective as the attack gets more targeted. If hungryhippos.ca were targeting Sally, rather than just any user wandering past their web site, they might be able to craft more specific attacks.

Hybrid API and UI Protection

If you’re protecting your UI and your API differently, then a real conundrum is presented for modern web application development. Many people want to be able to call their own API from their UI, to develop sophisticated single-page web applications and other highly interactive UIs. So, how can you allow your client-side UI code to call your API, while still protecting the API from CSRF attacks?

  • Allow your API to accept UI authentication, like the SESSIONID cookie, but require that API calls made with that form of authentication have additional security checks on them. They can either check the Referrer header, or have user-specific data inserted into the API payload.
    • Downside: This requires that your API and your UI are pretty tightly coupled, since they would share authentication mechanisms. That works just fine for a lot of projects, but it’s an architecture no-no for some people.
  • Proxy all your UI API requests through the UI to your API. The UI will perform its normal authentication of the requests, and then insert the user’s API-key into the API requests before sending it to the API implementation.
    • Bonus points: If you implement OAuth, you can make your UI have it’s own OAuth tokens for performing API requests on behalf of the user. This is a very decoupled architecture where your UI is an API integrating application no different than any other (except possibly in provisioning the OAuth token). It’s a bit architecture-astronaut-y for most applications, but there are some real benefits.

Summary

The various mechanisms for protecting against a CSRF attack aren’t particularly complex or difficult to implement. However, they require some discipline to follow. In my experience, retrofitting an application with proper CSRF protection is difficult; individual edge cases pop-up that lead to disabling protection here and there, and eventually the system is hardly protected at all.

Read more about Cross-site request forgery on Wikipedia. Thanks to pentestmonkey for their article on arbitrary-content CSRF POSTs, which is the technique that really exposes APIs to CSRF attacks.




Are you missing out on the latest API news, advice, design ideas, and case studies? You might be, unless you’re subscribed to the Web API Design Tips and Tricks newsletter! Subscribers will get the earliest news, access, and discounts for the ApiSlice, the intelligent Web API documentation system!




* = required field


Be Sociable, Share!

9 Comments on Is your Web API susceptible to a CSRF exploit?

  1. John Sheehan
    2013/06/17 at 10:44 am (10 months ago)

    The Referer header can be easily spoofed and should never be relied upon to guard against CSRF.

    Reply
    • Mathieu Fenniak
      2013/06/17 at 11:00 am (10 months ago)

      Hi John,

      I appreciate your feedback, and thanks again for pointing out that my comments were disabled.

      How can the Referer header be spoofed during a CSRF attack? My impression is that since the attack has to proceed with a normal user-agent that the attacker has no control over, there isn’t any way to spoof the Referer header.

      Reply
  2. Jonas Vestberg
    2013/06/17 at 12:59 pm (10 months ago)

    “The simplest choice for user-specific data is the user’s session ID. That is easy to validate on the server-side.”
    Rendering the session token in HTML forms will make it possible to bypass HttpOnly attribute if it is set on the session cookie. Best practice is to use a unique random token (often called anti-forgery token) for each session.

    Reply
  3. Luke Cyca
    2013/06/28 at 9:30 am (10 months ago)

    The Referer header can apparently be spoofed in the browser using flash. http://www.securityfocus.com/archive/1/441014/30/0/threaded

    Those specific vulnerabilities may be patched, but the possibility of other undiscovered browser plugin bugs makes it difficult to trust the Referer header for CSRF protection.

    Reply
  4. Taylor
    2013/07/19 at 1:13 pm (9 months ago)

    IMO the Referer header SHOULD BE relied upon to prevent CSRF attacks. It should not be able to be spoofed in a CSRF scenario – if it is being spoofed a client is not implementing it properly. Standards should not be built around the idea that they might not be followed.

    That being said if I’m building a banking website I’m putting a few more security checks in :-) .

    Reply
  5. Roger Qiu
    2013/09/25 at 7:32 pm (7 months ago)

    I like this method: Proxy all your UI API requests through the UI to your API. The UI will perform its normal authentication of the requests, and then insert the user’s API-key into the API requests before sending it to the API implementation.

    But if I was using OAuth 2 legged between the browser client to server API along with OAuth 3 legged between the browser client to the server API to the third party provider, how would this work?

    For example are you able to answer these two questions in relationship to each other?

    http://stackoverflow.com/questions/18999598/oauth-and-cookies
    http://stackoverflow.com/questions/2392100/how-to-prevent-csrf-in-a-restful-application

    If the UI client (JS based) is the one that handles the oauth access tokens, who handles the auth code exchange to access tokens (should this be done completely by the UI or exchanged server to server)? And can the CSRF code be the same as the access token?

    Reply
    • Roger Qiu
      2013/09/25 at 7:34 pm (7 months ago)

      And to follow up, should this access token be a query parameter, header or cookie? Perhaps not cookie since not all HTTP clients have cookies enabled.

      Reply

Leave a Reply