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 Answer 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

         // wrong; do something


Sending the header before processing it:

    $("#some_div").submit(function(event) {

      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, 2016 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, 2016 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, 2016 at 4:49
  • @Jeroen-ITNerdbox is it a good idea to use jwt (jwt.io) to generate the token? Mar 18, 2016 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, 2016 at 5:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .