4

When a client makes the first request, I send a session ID cookie generated by the server as a string of 64 random bytes using getrandom(2) or /dev/urandom, stored in the database, with the flags HttpOnly, Secure and SameSite=Strict set. Additionally, the server sends a Content-Security-Policy header with the value default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self', base-uri 'self'; form-action 'self'; frame-ancestors 'none', and it doesn't send any Access-Control-Allow-Origin header. I have no subdomains, all state changing requests are POST requests, and all data is properly escaped, which makes XSS impossible.

If a client makes a request in the future, I check if the request contains a session ID cookie and if that session ID is in the database, otherwise I ignore the session ID they send and create a new one as described above.

In this scenario, what advantages would a CSRF token provide, if I want to protect against someone impersonating someone else?

I can't think of any way in which an attacker would be able to send a request that the server would think belongs to another user. In order to do that, they'd need the user's session ID, and I can't see how they could obtain it, unless the user uses an old browser which doesn't understand the cookie flags or the Content-Security-Policy header, but I can protect against that by requiring the user to use a browser version known to support both.

If the CSRF token indeed doesn't provide any extra layer of security for the purposes of defending against impersonation, is there any other benefit of using a CSRF token in this scenario?

1
  • As you noted, not all browsers support the SameSite attribute caniuse.com/same-site-cookie-attribute so your site would still be vulnerable for people using those browsers. Other than that yeah, SameSite=Strict is enough.
    – Ajedi32
    Jul 13, 2021 at 20:33

2 Answers 2

2

Depends how confident you are in the "no subdomains" and "no browsers too old to understand SameSite cookie flag" conditions. Both of those have several ways to break them.

The "no subdomains" thing requires not only that you not have any subdomains, but that nobody else is able to register one for you (e.g. by gaining access to your domain registrar account), or create one for you using forged DNS responses or other DNS spoofing/poisoning. The latter case can't be directly protected against, but you can use HSTS with the includeSubDomains option to make it impractical (also, you should be doing that anyhow, to prevent SSL stripping and other HTTP attacks). In fact, given what you've told us, SSL Stripping is the thing that seems most likely to have not been considered.

Blocking browsers that don't understand SameSite isn't too hard, since all modern browsers do. Some people still use IE11, which is the most modern-ish browser that doesn't, but Microsoft is trying hard to end IE usage and you shouldn't feel bad about not supporting it. Still, you might want to use a list of known good browsers, rather than a list of known bad ones, if you want to pop up a message warning people. By the way, if you don't allow any state-changing actions via GET, then SameSite=strict is overkill and you can get a better user experience (e.g. if somebody wants to open a link into your site from another site, and have your site recognize their active session) by using SameSite=lax.

Assuming that both of the above conditions hold, you don't need additional anti-CSRF protection. However, why not add some anyhow? Anti-CSRF tokens don't have to be complicated to add; one of my favorites is requiring that the user submit a cryptographic hash (e.g. SHA2-256) or keyed hash (e.g. HMAC-SHA256) of their session token, with optional server-side key. This doesn't require any additional state for the server to store per user, is fast to compute, and is safe. However, if you already are using JS (rather than HTML forms) to submit requests to the server, there's an even easier option: just require that the client submit any non-standard header. It doesn't even have to be a secret or a user-specific value For example, if the client tries to add the custom HTTP request header CSRF: sucks to a first-party request, it'll just work. If they try doing that with a third-party request, though, the browser will first attempt a CORS pre-flight to verify whether the custom header is allowed. Since you're not responding to CORS preflights in any useful way, third-party sites can't interact with your site so long as the server requires that header on every [state-changing] request.


CSP doesn't meaningfully protect against CSRF (it protects against XSS and clickjacking), and anti-CSRF tokens don't protect against XSS (except maybe in the weird case of self-XSS), so CSP and anti-CSRF tokens are totally orthogonal.

Furthermore, CSRF doesn't exactly allow impersonating somebody - typically only XSS and session hijacking and maybe extremely badly configured CORS do that - but it does allow a "confused deputy" attack, where the victim does something they shouldn't / didn't mean to, because somebody else told them to. That's pretty similar to impersonation but not quite the same. In any case, there are lots of other attacks that aren't either of these, ranging from information-leaking attacks like cross-site search (which SameSite should mitigate) to various flavors of server-side attack like path traversal on file read/write or SQL injection or deserialization attacks. Don't focus too much on blocking just one kind, or even just one cluster, of attacks.

0

Yes, you still need it.

Yes, I can’t think of a way to break your site today, but that isn’t the security question, and this is a security site.

No, there are no other security benefits to using a csrf token. You could look at crypto systems that use nonces, or anti-replay stuff but that’s not what the csrf is doing.

The security question is should I use CSRF to keep my site safe? Yes you should because it’s an extra layer that costs nothing.

That makes this a risk question. Is the cost of the mitigation greater than or less than the cost of the risk?

(Also you must evaluate whether the mitigation is more harmful or less harmful than the risk. Otherwise you force people to rotate passwords every 90 days because everyone does it - even though it’s more harmful than not rotating them)

Reasons to do it:

  1. If it breaks, we don’t know what will break, or what the impact will be. I can’t think of a way to break this but risk is impact AND probably. This point is the impact is not known.

  2. The sheer number of things you have to do to get the site to a state where csrf is probably safe tells me:

2.a. at some point now or into the future, somebody will make a mistake.

2.b. I don’t know if the browser developers have actually tested all of these states on all of the browsers.

So the probability that someone will mess up one of these numerous defenses - either someone working on your code, or someone working on the browsers, is medium. (Tough to argue it probably won’t happen. Tough to argue it will.)

Typically an unknown impact.

An unknown impact and a medium probably is a high risk.

Since csrf is easy (code in every popular browser and backend I’ve used in the last few years), I believe continuing to use CSRF is the right path forward.

Further, and this is a stretch - consider that it’s easy to do the csrf stuff, and it’s practically ubiquitous. it’s possible that browser developers may not prioritize bugs in the other solutions. This might leave your implementation vulnerable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.