13
votes

I am currently generating a CSRF token in my forms to prevent cross-site request forgery. It looks like:

<form method="post" action="action.php">
   <input type="hidden" id="security_token" name="security_token" value="gTt96phAcretR99rafEjepHebrEZadEdezadagaZ3gAS5es33WReJeZaMADU2AWr" />
   ...
</form>

The problem is that I have multiple forms on a single page. Must I create a security token for each form so security_token_1, security_token_2, or can I simply instead of generating the security token inside of forms, append it to a property to the entire body tag like:

<body data-csrf-token="gTt96phAcretR99rafEjepHebrEZadEdezadagaZ3gAS5es33WReJeZaMADU2AWr">
...
</body>

Is this insecure in any way? It simplifies things quite a bit, since I can simply append the security token to the body element instead of dealing with multiple security tokens.

Thanks for the insight and comments.

2

2 Answers

16
votes

There really isn't any reason you can't have the same generated token for both forms, with each hidden field in each form having the same name attribute.

After all, what you are really trying to validate is that a form request is inbound from a user with a valid session, and only one form is going to be actively posted at a time. Thus you are comparing the token posted against a token stored in session for the user. There need not be more than one token value in order to do this.

For your case of needing to update the tokens based on AJAX posts, what you would need to do is, as you say, pass the newly created token back in the AJAX response, then update the hidden field values to the new token value.

3
votes

As long as you make sure that attackers cannot get the CSRF token (e.g. don't include it in forms that go to third parties), using a single CSRF token should be OK. Note that with your "put it into the body" method, you will need JavaScript to submit the forms, so you may still want to insert it into every form even if you use just one central token.

The OWASP Guide says:

In general, developers need only generate this token once for the current session. After initial generation of this token, the value is stored in the session and is utilized for each subsequent request until the session expires.

i.e. there is no need to use more than one token at all.

Using separate tokens just has the advantage that if you somehow managed to compromise some, but not all of them (pretty unlikely, because for example XSS will compromise all of them), the attacker's options would be limited.