Skip to content

Instantly share code, notes, and snippets.

@yashap
Last active April 12, 2020 00:58
Show Gist options
  • Save yashap/e5d23c2e413467fd2ba39159a28e1177 to your computer and use it in GitHub Desktop.
Save yashap/e5d23c2e413467fd2ba39159a28e1177 to your computer and use it in GitHub Desktop.

Notes on CSRF

What is CSRF?

CSRF attacks are basically session hijacking. Say I'm logged in to foo.com, which has an endpoint like POST http://foo.com/api/password { "password": $newPassword }. Obviously this endpoint will fail if I'm not logged in, but that's not enough protection on its own, assuming the proof of my login is stored as a session cookie. An attacker could set up a site like attacker.com, and on load of the attacker site, it automatically makes a request like POST http://foo.com/api/password { "password": "uHackedNow!" }. If I visit http://attacker.com while logged in to foo.com, and foo.com doesn't have CSRF protection built in, I'm screwed, because the browser will automatically send along my foo.com cookies (including the session cookie) on requests to foo.com, and thus the attacker is able to change my password.

Preventing CSRF Attacks

The below is a quick summary of notes from:

An example strategy for preventing CSRF attacks:

  • On GET request for a PAGE (i.e. getting the HTML, not an Ajax GET, media GET, etc.)
    • Is there already an anti-forgery token cookie set?
      • If so, do nothing, and just use the cookie value as the anti-forgery token
        • Though probably verify the format (i.e. if you expect it to be a UUID, ensure that it is)
      • If not set
        • Set an anti-forgery cookie on the response object
          • Could just be a new UUID
        • Ensure the cookie is HTTP only
          • i.e. can't be read by JS
        • If it's a secure request (HTTPS), set a secure cookie
          • i.e. the browser will only send it back on subsequent secure requests
    • Set the anti-forgery token in the page itself
      • Like in an HTML meta tag or something, i.e. <meta name="csrf-token" content="$antiForgeryToken">
      • Or could set it in a JS variable via a script tag, i.e. <script type="text/javascript">var csrf_token = "$antiForgeryToken";</script>
      • Or for forms, you set it as hidden input, i.e. <input type="hidden" name="csrf_token" value="$antiForgeryToken"/>
    • If the user is logged in, also set the user id in the page itself
      • Similar to how you set the anti-forgery token
      • Alternately, you could encode both the anti-forgery token and the user id in a single value on the page
  • On any POST/PUT/PATCH/DELETE
    • On the FE, when making the request, ensure that the page data (anti-forgery token and user id) is pulled out of the page and sent along with the request
      • For an Ajax request, this often means pulling them out of the meta-tag/js-var/whatever, and setting them as header(s)
      • For submitting a form, this means putting these values in the body of the POST
    • Obviously, the cookie value of the anti-forgery token is automatically sent with any request
      • You don't need a cookie version of the user id, since it'll be something you can derive from authenticating the user's session token, which you're doing anyways, outside of CSRF protection
    • On the BE, validate that both the page anti-forgery token and cookie anti-forgery token are equal
    • If the user is logged in, also validate that the HTML user id is equal to the logged in user id

The key point is that the attacker CAN modify the page token (i.e. their attacker page can send any sort of token in a header, form body, however we do it), but they CANNOT read or modify the HTTP-only cookie token. And they also cannot guess what the cookie token will be (it's something like a UUID set on the first page load in the session). So they can't make the page token equal to the cookie token, and thus their request fails.

Note that, if your site has an XSS vulnerability, then the attacker CAN read/modify HTTP-only cookies, so then you're still screwed, but you're probably screwed anyways if you have an XSS vulnerability.

Questions

  • Doesn't the browser's Same Origin Policy prevent CSRF?
    • No, the SOP only prevents READING RESPONSES from another domain, not things like POSTing data
    • Thus validation of anti-forgery tokens only has to happen on mutating requests, not on reads
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment