Django allows you to specify that the session expires at browser close (with some caveats for Chrome). Why doesn't it do that for the CSRF cookie?
I ask because it seems to me that the CSRF token is vulnerable to being leaked (e.g., by mistakenly putting it in a post to an external site), and this would be a mitigation for that. Am I misunderstanding something?
I'll repost my answer from the developer's list that Carl linked, so that stackoverflow has it too:
If the cookie were set to expire at browser close, it would cause CSRF
errors for users who closed a browser (or bookmarked a page with a
form on it) and then loaded that page from a browser cache and
submitted the form. I'm ambivalent about whether this use case is
worth supporting (it may be important on mobile devices, for example),
but I don't believe that setting the cookie to expire on browser close
provides much security benefit to an otherwise properly configured
site (HTTPS, HSTS, etc.).
Django's CSRF implementation differs[1] from many others which store
CSRF information alongside session information on the server. The CSRF
mechanism functions by matching a token provided in a form with a
token provided as a cookie in the browser. If you set the cookie to
'zzz', it will still function perfectly well. The security comes from
the fact that an attacker cannot set the cookie, not that it happens
to contain any specific cryptographic value.
If the concern is that an attacker could access a user's physical
computer between sessions and steal a CSRF token, setting it to expire
at browser close would not prevent an attacker from inserting a cookie
of known value that would be used during the next session. I'm not
convinced we can secure the tokens of a user whose computer has been
physically accessed by an attacker.
Still, if it can be convincingly demonstrated that setting the cookie
to expire at browser close would not break existing use cases (mobile
browsers are my chief concern) I'd be open to changing the default
behavior. We generally consider it a bug if any non-malicious user
can, through innocent behavior, trigger the CSRF warning.
[1] Django's CSRF implementation usually sets off all kinds of false
alarms in most pen-tester tools, since it doesn't work exactly the
same way other implementations do, and isn't tied to the session
cookie.
Related
I just read an article from this
https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/
In summary, they recommend to store JWT Access Token in memory (as a variable in JavaScript for example) and Refresh Token in HTTP-Only Cookie.
They said:
But by persisting our session indirectly via a refresh token, we
prevent a direct CSRF vulnerability we would have had with a JWT
token.
But is it store Refresh Token in a HTTP-Only cookie still vulnerable to CSRF Attack? For example, evilsite.com can make request to /refreshtoken endpoint to get new JWT access token. What I understand about HTTP-Only cookie is the cookie can't be read from JavaScript but it will automatically be sent whenever make a HTTP request like how the evilsite.com does. (Correct me if i'm wrong).
Refresh token in a cookie and access token in memory can be a good model if used with care. Hopefully some better guidance will be made available in standards such as BFF-TMI.
The cookie should have these properties and the SameSite property will mean evilsite cannot send it, so that it is good from a CSRF viewpoint.
HTTP Only
Secure
AES256 Encrypted
SameSite=strict
OWASP's CSRF Guidance seems to recommend using both SameSite and CSRF tokens together as the preferred option.
Out of interest here is how I'm implementing things in an SPA Code Sample at the moment - in case it is useful to borrow ideas from:
SPA OAuth Code
Back End API OAuth Code
I may change my mind a little as guidance in this area evolves.
HIGH SECURITY OPTION?
The current concensus is that the following option is preferred in terms of security, though I am hoping risks and mitigations become better documented over the coming year:
Keep access tokens out of the browser completely
Proxy all API calls via the 'back end for front end'
See this insightful 2021 video
This option may add complexity in some areas though, and could also limit capabilities in some scenarios.
As is often the case with security, there are trade-offs with other factors, and the solution chosen may depend on data sensitivity and what stakeholders care about.
Although there are many posts about this topic (or closely related) on SO, I did not find what I am looking for.
As the title suggests I am using Django Rest Framework as a backend, and React as a frontend.
Now I implemented token authentication, and it works perfeclty. The only problem is that the token is stored in React's state, and if the user refreshes the page, he is no longer logged in (the token is lost).
So, now I want to switch to session authentication, since the problem is solved then. But that will require me to do some research, and before I go there I'd like to know if that is the best choice.
My question:
Do I need to use session authentication to have users stay logged in, even when the React's state changes. Or can I also achieve the same thing with token authentication (in a safe and responsible way?)
I figure I can save the token in a cookie, but that doesn't seem safe to me.
EDIT:
Later I realized, why not just store the token in a session?
SessionAuthentication would be the most straightforward way to achieve what you want. The configuration is described at http://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme This will set a session id in a cookie that is handled by the browser.
Alternatively you can store a token in the browser's cookie but that is vulnerable to XSS attacks and other javascript attacks. For more security you can store the token in an HttpOnly cookie. The cookie would be persisted across tab/window closes by the browser.
Also to clarify cookie handling is built into most browsers. Your react state is in userland and lives in a different memoryspace than cookie storage.
More info:
Ask HN: Cookies vs. JWT vs. OAuth
https://developer.okta.com/blog/2017/08/17/why-jwts-suck-as-session-tokens
http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
Django's CsrfViewMiddleware sets "Vary: Cookie" header, that means that cache system will take into account not only page URL but also user's Cookies that are unique for each user.
So pages don't cache once for all users, but for each user. And in my case I have very loaded site, and such behaviour does not satisfy me.
Do I have right view on this issue, or I'm wrong?
Can I turn off setting "Vary: Cookie" header without turning off CSRF protection?
Yes, you have the right view on this issue. When you use Django's CSRF-protection for a view, not only are the cookies unique for each user, but also the page content because every CSRF-protected form has a hidden field csrftoken.
You could work around this issue by setting the value of the csrftoken field to match the cookie on the client side with JavaScript, but this is not provided out of the box by Django.
However, you will have to ensure that:
your users actually does get a unique CSRF token somehow
as noted by #patrys, CSRF tokens are never accidentally shared between users through the cache (e.g. by stripping Cookie headers from responses)
There are several opportunities to shoot yourself in the foot and make your site susceptible to CSRF attacks.
It will compromise your site's security if multiple users access the site through a caching proxy.
The proxy will see that the response does not depend on the cookies and will serve the same response (along with the same CSRF token in the contained hidden field and in cookie headers) to all its users.
Since all users share the same secret, they are now all open to each other's cross-site resource forgery attacks.
Also it's very likely that each view will end up being cached with a different CSRF token and accessing such URLs in parallel (in a different tab, in an iframe or using AJAX) will overwrite user's cookies and thus make it impossible to submit a POST request.
I have a Django Site that uses Django's csrf-token for protection against csrf attacks. One of the forms can be accessed by public, including people who have not logged in.
Csrf Token is supposed to give protection against cross domain requests.
Edit: (quote from my comment)
"But, in case of post requests that are allowed without requiring authorization, csrf is no better than a trival spam filter(captcha would do better here). In fact, it should be a security risk to include CSRF token(that expire after say, 30 mins) in pages that require no authentication what so ever.(but my site is doing it, which is why I made this post in the first place)"
Also, in this case, one could just fetch that page in browser js console, get the csrf token through some specific xpath and then post some arbitrary data with that csrf. Also, steps being easily reproducible, one could design a specific attack for the site, or any Django site for that matter cause you'll find csrf token besides 'csrfmiddlewaretoken' every time (and that includes sites like reddit, pinterest etc.).
As far as I can see, apart from making it a little difficult, csrf token didn't help much.
Is there an aspect to it I am missing? Is my implementation wrong? and If I'am correct is it dumb to have your csrf token flying around in your html source(esp. those not requiring any authentication)?
This question has a really good couple of answers about the same thing. Also, the last answer on there addresses the fact that it technically would be possible to scrape the form for the token (via javascript), and then submit a post request with it (via javascript). But that the victim would have to be logged in.
The point of the CSRF protection is to specifically prevent tricking a random user. It has nothing to do with client-side exploits. You also have to consider that part of the protection includes denying cross-site origin requests. The request would have to come from the same origin as the target site.
Bottom line, CSRF has value. Its a region of protection, but its not the end all be all. And you can't defend against everything.
Quote from a blog post about CSRF:
Secret hidden form value. Send down a unique server form value with
each form -- typically tied to the user session -- and validate that
you get the same value back in the form post. The attacker can't
simply scrape your remote form as the target user through JavaScript,
thanks to same-domain request limits in the XmlHttpRequest function.
... And comments of interest:
I'm not a javascript wizard, but is it possible to load a remote page
in a hidden iframe on the malicious page, parse it with javascript to
find the hidden token and then populate the form the user is
(presumably) about to submit with the right values?
David Goodwin on September 24, 2008 2:35 AM
#David Goodwin: No, the same-origin policy would prevent the malicious
page from reading the contents of the iframe.
Rico on September 24, 2008 3:03 AM
If your form is public and doesn't require authentication, then there is nothing stopping anyone (including malicious sites/plugins/people) from posting to it. That problem is called Spam, not CSRF.
Read this: http://en.wikipedia.org/wiki/Cross-site_request_forgery
CSRF involves a malicious site posting to your forms by pretending to be an authenticated user.
ColdFusion sessions are supported with a combination of CFID, CFTOKEN and jsessionid values. When a cfm page is first hit, these values are established thus creating the SESSION.
My question is, if the SESSION is created under HTTP and then a link is clicked to get to a login page under HTTPS, are those SESSION token values compromised because they were created under http (i.e. they were passed in clear text as part of the request).
I'm guessing that someone astutely sniffing the a public router could get those values and then spoof the session from then on out. It would definitely be a rare occurrence, I know, but nevertheless a concern.
Yes, your cookies will be vulnerable to eavesdropping and session hijacking if you pass them over a non-secure channel. Wikipedia has some good prevention mechanisms listed on their Session Hijacking page. Probably the easiest is to do as invertedSpear said and just regenerate the session after a successful login, and once logged in, stay on HTTPS.