-3

Say we implement CSRF tokens that expire when the session expires. A user opens a website and logs in. The session expires. Then in another tab, they log in again, go to the expired tab, and try to perform an action that requires a valid token. How could we avoid hindering the user from making the request while maintaining security?

I was thinking perhaps we could preserve the user's intent, and some how trigger a chain of events that securely leads to another fresh token being automatically obtained and then submitted with the original attempt.

Is that feasible? How would that back-and-forth work? And how would would the CSRF token have to be communicated, in all directions? Cookie? Body? POST? Does it matter? Would it rely on CORS headers for security, or is it inherently secure?

Couldn't that request be mimicked by a CSRF attack? Because that request would surely mimic a CSRF with an old, leaked token, defeating the purpose of expiring tokens?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Jodes
  • 14,118
  • 26
  • 97
  • 156
  • You could obtain a fresh CSRF token before _every_ request that needs one. This is most convenient for the user, but doubles the number of requests you make. – Heiko Theißen May 18 '23 at 15:46
  • @HeikoTheißen thanks but I don't understand how that would work. Say the user clicks on a link to perform an action, are you saying that at that instant I should for example use ajax to fetch a token, then send that token in the url for the link click? – Jodes May 18 '23 at 20:25
  • Actions should be triggered by forms, not links. See my answer. – Heiko Theißen May 19 '23 at 07:33

1 Answers1

2

CSRF tokens are necessary to protect "simple HTTP requests" like the ones that result from the submission of an HTML form, because such requests cannot be protected by a CORS preflight request.

The CSRF token can be included in the <form> element when the form is loaded, but it may then have expired by the time the form is actually submitted, as you describe in your question. To prevent this, obtain a fresh CSRF token before submitting the form:

async function csrftoken(form) {
  event.preventDefault();
  form.csrf.value = await (await fetch("csrftoken")).text();
  form.submit();
}
<form method="post" action="transfer" onsubmit="csrftoken(this)">
  <input type="hidden" name="csrf" />
  <input name="amount" />
  <input type="submit" value="Transfer" />
</form>
Heiko Theißen
  • 12,807
  • 2
  • 7
  • 31