Search code examples
formscsrf

csrf hash token behaviour when website is open in multiple tabs


I am trying to integrate a method to add a hash token to every form in my application. I want to achieve to goals with this:

  1. Prevent csrf attacks
  2. Prevent that a form gets resubmitted when the user reloads a page after submitting a form

Now, the concept in doing this should be simple enough I thought:

  • I generate a unique hash and save it to the cookie
  • I create a hidden field in the form with the generated hash
  • before processing the $_POST data from the form I verify that the hash value from the form matches the one from the cookie.

So far so good, now what where I really get stuck is the following scenario:

What if a user opens another tab with with my application. Everytime the page gets loaded the hash value gets regenerated. Thus rendering the hash value from the form in the first tab invalid.


Solution

  • Don't store the token as a cookie.

    Generate a unique token on each page impression. Upon POST, verify that YOU issued the token (to this user) and verify that it hasn't been used before.

    First, we generate a token where we can verify that we issued it for a given user:

    token = hash(session_id + secret)

    That way, using their session id and our secret, we can always verify that we issued that token, since noone knows secret.

    Now we need to make sure that the token can only be used once.

    rnd = rand()
    token = rnd + hash(session_id + secret + rnd)
    

    The token now has a random number. When a POST happens, we can store this random number as "has been used before" and reject any token that re-uses the same random number.

    But we don't want to store random numbers of used tokens forever. So we limit the lifetime of tokens.

    rnd = rand()
    now = time()
    token = rnd + time + hash(session_id + secret + rnd + time)
    

    On POST, when we get a token we now check if we issued it "recently". We only need to store used random numbers for that same timespan. All older tokens are invalid by definition.

    You can safe the used random numbers along with the session id and delete them when you evict the session id or when they become invalid (whichever happens first).