I'm trying to implement the Double Submit Cookie pattern with extra protection using encrypted or signed CSRF tokens. I'm working with a Single Page Application and a stateless API. The purpose of this is to reduce risk of an attacker setting the tokens. This should be implemented with HSTS too.

I understand the non-encrypted workflow of Double Submit Cookie (No HMAC, No encryption):

  1. The user goes into the page and gets a token (preferably before login to avoid login CSRF). The token is passed from the server to the client as a Http-Only=false cookie. Let's call this "Cookie A".

  2. Whenever the client is going to send a request, it reads the value of "Cookie A" with JavaScript and attaches the token as a hidden form field or as a HTTP header. The client also sends "Cookie A" to the API with the request.

  3. The API validates that the value of "Cookie A" matches the value from the form field or the HTTP header.

Please correct me if these steps are wrong.

In the encrypted or HMAC version of the process, I don't understand how the server sends both the encrypted/HMAC AND the plain-text CSRF token to the client.

  • Is the server supposed to send 2 cookies? 1 encrypted/HMAC and 1 plain-text?
  • Is the encrypted/HMAC cookie Http-Only=true?
  • Does HSTS render encrypted/HMAC cookies useless?

There's no further information in OWASP about how to implement this. Further guidance is much appreciated.

  • Why would you set this as HTTP-ONLY=FALSE?? And why would you let client-side JS create the token?? As I understand it, the point of the encryption for the hidden field value is so only the server can create and decrypt that hidden field value which can then be evaluated against the cookie. (and you can create a new key/value for each form if you like... the cookie can remain the same and be refreshed only when the session is) Feb 10, 2023 at 19:21
  • @pcalkins to summarize the question: How would you send the encrypted token and unencrypted token from the server to the client? Would you use 2 cookies? If so, which attributes would these cookies have for domain, path, sameSite, httpOnly... OWASP does not clarify these questions.
    – AFP_555
    Feb 11, 2023 at 6:29
  • double-submit uses 1 cookie, and 1 parameter (usually a hidden field in the form). You'd set the cookie when a session is begun. Same domain or it won't be sent automatically with each request. samesite, httponly, and secure options are very good options to set for security. The hidden field inside the form ties the session to the form. It's harder to impersonate... since this value changes.... either with each new session or with each request for the form. There are also alternative techniques which use custom headers. Feb 11, 2023 at 16:51
  • I think the confusion comes with the session cookie and the anti-csrf cookie. Those would be the 2. The server can send a request for the browser to store a cookie.... then it will send another request for redirect. Those two things can also be sent via javascript... (the server would send back some data... the JS writes the cookie, then updates the DOM accordingly) either way you want those requests to be secure and same domain. Feb 11, 2023 at 16:57
  • @pcalkins OK, so 1 cookie is the session ID and the other cookie is the CSRF token. I'm working with a Single Page App and JWT tokens for Auth. Also, the API is stateless. I'm not sure if I should have sessions.
    – AFP_555
    Feb 11, 2023 at 19:44

2 Answers 2


I have implemented this using HMAC and below are the steps:

  1. Generate HMAC using session_id, timestamp with 19 digits, logged in user_id
  2. Now prepend the same timestamp to the token above, timestamp + hmac_hash
  3. Set that token from step-2 in cookies as CSRF token
  4. FE will read the csrf token from cookie and pass it in header
  5. BE will read the csrf token token from header
  6. Extract the timestamp -- first 19 chars from csrf_token
  7. Extract the HMAC hash which is a string after first 19 chars.
  8. Re-generate hmac hash using session_id, user_id, timestamp
  9. Securly compare the computed hash against the hash present in point 7. There can be a function in your technology to compare them securly.

Unfortunately, this is the top result on google for HMAC double submit. So I will leave this here.

OWASP cheat sheet includes a section on HMAC double submit including some pseudocode. I would suggest that anyone looking for information starts there.

I think a lot of confusion in the comments stem from a misunderstanding of what double submit is for. We are not simply trying to prove that the user's browser has the cookie that we sent it. Session cookies already do this. We are trying to prove something different: that the origin of the call can read the token. We are basically piggybacking on the browsers same-origin policy.

Is the server supposed to send 2 cookies? 1 encrypted/HMAC and 1 plain-text?

That would be pointless, just send both as a single cookie as proposed in the owasp cheat sheet.

hmac + "." + message

Is the encrypted/HMAC cookie Http-Only=true?

You can set to true and template the token into a html form or set it to false and read it with javascript to than add it as a custom header. In this case setting to false and using javascript is more secure since requests with custom headers are always subject to same-origin policy.

This goes back to my point about what we are trying to prove. This is not a session cookie, it's not a protection from XSS or any other attack vector. This only protects you from CSRF, nothing else. Proving that you can read the token while subject to the same-origin policy is the whole point of double submit.

Does HSTS render encrypted/HMAC cookies useless?

Not totally. HMAC stops an attacker who can set cookies. HSTS only prevents the most common way for an attacker to set cookies (through a subdomain) but there are other ways. see: https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf

As to why this matters: The problem with the naive approach is that we accept any token as long as the one in the cookie and the one in the request match. This means that an attacker who can set cookies can just put any random value in there and send a matching request. We prevent this by signing the token and tying it to some session-specific value. So only a token generated by the server for this specific session will be considered valid.

You must log in to answer this question.

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