
Close Look at CSRF Tokens - lobo_tuerto
https://denvaar.github.io/articles/csrf_tokens_with_phoenix.html
======
chrismorgan
Related:

• Cross-Site Request Forgery is dead! (2017): [https://scotthelme.co.uk/csrf-
is-dead/](https://scotthelme.co.uk/csrf-is-dead/)

• CSRF is (really) dead (2019): [https://scotthelme.co.uk/csrf-is-really-
dead/](https://scotthelme.co.uk/csrf-is-really-dead/)

If your use of CSRF tokens is purely for preventing cross-site request forgery
(and not things like form expiry or double-submit protection, which are
commonly paired with it, for better or for worse) then CSRF tokens are
genuinely obsolete and unnecessary, and you should set your SameSite attribute
on your cookies to ensure you’re protected against CSRF in the most browsers
(meaning all browsers from at least the last couple of years—including IE11
updated within that time, by the way—rather than probably just Chrome at
present).

~~~
nmadden
This isn't true. Although SameSite is a good defence in depth, it provides no
protection against CSRF from sub-domains (subdomain hijacking is fairly
common), or in older browsers. It also is incompatible with some site features
- e.g. OIDC form_post response mode, SAML SLO with HTTP POST binding, etc.

~~~
chrismorgan
Subdomain hijacking is only possible if you’ve allowed subdomains to be _used_
for such other things, which I strongly assert is _rare_.

Older browsers: all browser releases from the last couple of years support it
(including IE11); browsers from before then should not be being used on the
public internet under _any_ circumstances. It should be a negligible fraction
of your user base that uses such browsers, and so long as you’re also happy to
break IE11, a safe path (from a security perspective) is to actively break the
site for old browsers by blacklisting them or checking for support of a
comparatively recent feature. (Just don’t do a whitelist by user agent string,
that approach is unmitigatably bad.)

You’re correct about some auth systems not playing nicely with strict same-
site cookies, because they’ve been designed with certain assumptions in mind.
I’m not particularly conversant with that space, never having had to interact
with such a system.

~~~
nmadden
Subdomain hijacking is not rare for the simple reason that websites are built
by marketing departments. E.g., here’s Hanno Böck tweeting about a subdomain
takeover he found at _Kaspersky_ just recently:
[https://twitter.com/hanno/status/1257958739132514304](https://twitter.com/hanno/status/1257958739132514304)
There’s a whole cottage industry of people finding these vulnerabilities on a
very regular basis: [https://www.hackerone.com/blog/Guide-Subdomain-
Takeovers](https://www.hackerone.com/blog/Guide-Subdomain-Takeovers)

~~~
chrismorgan
Which is one reason why a best practice (and a very common practice, though a
long way off universal) is to make sure that the cookies are scoped to your
app only, e.g. by putting your app on a subdomain of its own and scoping your
auth cookies to that subdomain.

~~~
nmadden
That doesn't impact CSRF at all. If I send a request from a page on
foo.example.com to api.example.com your browser will send those cookies no
matter how tightly scoped they are because the _destination_ of the request
matches the cookie. It will send those cookies whether you use host cookies,
add a __Host_ prefix, set SameSite=strict, Secure, HttpOnly, etc, etc, etc.

If you want to protect against these attacks with basic cookie attributes you
can use SameSite _and_ register your site (and any intermediate sub-domains)
on the public suffix list to effectively shun your subdomains as being
completely separate sites.

~~~
chrismorgan
If you’re going from foo.example.com to api.example.com, sure, SameSite won’t
protect you because those two origins are considered same-site. So don’t do
that: keep your API calls on foo.example.com, with domain=foo.example.com
cookies.

Also, I seem to recall the PSL maintainers requesting at some point that
people not do what you describe, because it wouldn’t scale at all.

~~~
nmadden
Firstly, setting the domain attribute on a cookie explicitly allows sub-
domains. You want to leave that off to enable host-only cookies. But secondly,
as I just said the host/domain on the cookie does nothing at all to prevent
cross-origin requests to that domain, which is the relevant concern for CSRF.
Setting the domain/host is completely irrelevant to this attack.

Yes, adding to PSL is not at all scalable and will likely cause you a world of
pain down the road. It was not intended as a serious suggestion. Use proper
CSRF defences.

------
mac-chaffee
I've found that a key part of CSRF protection is ensuring it gets added
everywhere consistently. When new features need to be done yesterday and you
have to review code from 20+ junior developers, it's easy to miss that extra
hidden <input> field.

That's why I love if it's built into the framework like Django or Phoenix
rather than bolted on or home-rolled.

~~~
silviogutierrez
Big Django user. Isn't it still required that you add the {% csrf_token %} tag
to your forms?

That is, the _protection_ will be on. But the way to be allowed in by the gate
is not on by default. You have to remember to add the tag.

Which is why I'm so excited to just switch to SameSite once there's enough
browser usage...

~~~
mac-chaffee
My Django is a little rusty but I think now requests will return 403s by
default if you forget to include the csrf_token tag:
[https://docs.djangoproject.com/en/3.0/ref/csrf/#rejected-
req...](https://docs.djangoproject.com/en/3.0/ref/csrf/#rejected-requests)

Whereas the hand-rolled CSRF scheme I've inherited will fail silently

~~~
silviogutierrez
That's what I mean. If you _forget_ to include the token on your app, it will
fail. You have to remember to call {% csrf_token %} in _every_ template.

Back in the day, the middleware scanned your response and injected it next to
each </form> tag. A hack and dirty for sure, but removed that burden.

~~~
StavrosK
It will fail _securely_ , which is what you want. The alternative is to have
it fail _insecurely_ and bypass validation for everything.

In the latest versions the framework sets the SameSite header properly, so you
don't need the CSRF middleware at all (you can just remove it).

------
tebruno99
What I don't understand is why store it server side? If we make a HTTP Only +
secure cookie containing the encrypted and signed csrf value, it can't be
obtained by an attacker & the server would require no state or shared cache
between instances. It would just compare the HTTP only cookie to the value
submitted with the form. On response, expire that cookie.

Does anyone know of a downside to that?

~~~
bluepnume
Yup. This is known as 'double submit'.
[https://cheatsheetseries.owasp.org/cheatsheets/Cross-
Site_Re...](https://cheatsheetseries.owasp.org/cheatsheets/Cross-
Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie)

~~~
tebruno99
Ah! Thanks, I've been looking for the exact definition!

------
Lx1oG-AWb6h_ZG0
Is any of this actually needed anymore? Setting the SameSite attribute on the
cookie will ensure the browser will not send it for cross-site requests, so...
problem solved?

~~~
StavrosK
CSRF protection isn't needed after SameSite, nope.

------
technics256
I still can't seem to get good informantion if this should be common usage on
something like React SPAs for things like signup forms and other forms
publicly POSTing data?

~~~
byteshock
The article below seems to suggest that as long as your api is on a subdomain
and is using CORS headers, you should be fine.

However I’m not so sure about this, as I still implement CSRF tokens in my
SPAs. (Bit of a habit from my php days) I store the tokens in local storage
and pass them through the request headers.

A lot of people have different ideas and methods when it comes to CSRF
protection on SPAs. I’d love to hear other people’s opinion and tactics!

Feel free to correct me on anything.

Article: [https://medium.com/tresorit-engineering/modern-csrf-
mitigati...](https://medium.com/tresorit-engineering/modern-csrf-mitigation-
in-single-page-applications-695bcb538eec)

------
globular-toast
Reverse engineering seems an odd way to learn about this when good
documentation is available, e.g.
[https://docs.djangoproject.com/en/3.0/ref/csrf/](https://docs.djangoproject.com/en/3.0/ref/csrf/)

~~~
tebruno99
Docs are a great way to overview. You never know what the actual
implementation is doing and I find that having actual hands on experience
helps me solidify the knowledge into relatable experience.

If you don't care why, read the docs. If you want to understand HOW, read the
code.

