1

There are a ton of articles explaining what CSRF is and that I should use a CSRF token to prevent CSRF. A few of them mention details such as using SHA256 over MD5. However, none of them explain how to implement CSRF protection in relation to the user flow. For example, am I suppose to store the CSRF token in a database or can it be self contained? Here's how I think I'm going to implement CSRF protection, let me know if there's anything wrong:

  1. User arrives at the page and logs in. They receive an http-only cookie containing a JWT.

  2. I generate a random 128-512 bit value (fixed length, I will decide how long it is). Then, I concat the value and the user's ID and HMAC SHA256 hash the result with a private key. I embed both the random value and the hash in the HTML.

  3. Whenever the user makes an API request, the random value and the hash are appended as either a header, GET variable, or POST variable (there's no difference to security right?).

  4. The server concats the given value with the user's ID and compares it with the given hash. If they match, then the CSRF portion is done.

Is this implementation correct? Some cause for concern is that the user receives a new CSRF token every time they perform a full page load. They can also have multiple valid tokens if they have multiple tabs open. I don't think these will cause issues though.

Edit: A whole day of reading about CSRF later, I realized I can just put the JWT in the API request header and that's it.

1

I'd recommend creating a system where an anti CSRF-token is submitted from a hidden input field and is transmitted in the header. Both should be checked on the server side.

Here's an example in PHP:

function generateToken($key) {
  $token = base64_encode(openssl_random_pseudo_bytes(16));
  $_SESSION['csrf_' . $key] = $token;
  return $token;
}


function checkToken($key, $value) {
  if (!isset($_SESSION['csrf_' . $key]))
    return false;
  if (!$value)
    return false;
  if ($_SESSION['csrf_' . $key] !== $value)
    return false;

  unset($_SESSION['csrf_' . $key]);
  return true;
}


  if ($_POST and $_POST['action'] == "something")
  {
    $header_token = apache_request_headers()['X-Anti-Csrf-Token'];
    $post_token = $_POST['token'];
    $post_token = str_replace(" ", "+", $post_token);

    if ($header_token == $post_token)
    {
        if (checkToken('settings', $post_token))
        {   
           // ok; do something

        }
     }
     else
     { 
         // wrong; do something
     }


 }

Sending the header before processing it:

   <script>
    $("#some_div").submit(function(event) {
      event.preventDefault();

      var $form = $(this),
        url = $form.attr('action');

      var posting = $.ajax(url, {
        type: 'POST',
        processData: true,
        dataType: "text",
        beforeSend: function (xhr) {
        xhr.setRequestHeader('X-Anti-CSRF-Token', $('#token').val());
    }

This piece of code will generate the token and puts it in an hidden input field.

<input id="token" type="hidden" value="<?php echo generateToken('settings'); ?>">
  • In your example, the server has to maintain a session. Is this necessary for CSRF protection or is it just for convenience? I'm currently not maintaining server-side sessions for my users. Also, what happens if the user opens 2 tabs with a form each? Wouldn't the first one be invalidated? – Leo Jiang Mar 8 '16 at 19:22
  • My personal opinion is that the server should always have the state. That's the only way to properly check (server-side) if the token is valid. In case of two open tabs on the same form, well if the second page load, a new anti CSRF token is generated so yes, this will work. – Jeroen Mar 9 '16 at 4:45
  • In addition to my previous comment: The form in the first tab will no longer work after the form is loaded in the second tab as the state on the server side is overwritten unless you create a random $key in the token generator. This could have other consequences such as resource exhaustion but the likelihood of that happening is quite low I think. – Jeroen Mar 9 '16 at 4:49
  • @Jeroen-ITNerdbox is it a good idea to use jwt (jwt.io) to generate the token? – Anthony Kong Mar 18 '16 at 5:39
  • As I haven't used this myself, I can't really say much about it. Apparently there was an issues with symmetric keys: (auth0.com/blog/2015/03/31/…). Please note this is not a standard yet, it's a proposed standard. – Jeroen Mar 18 '16 at 5:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.