In Django world, CSRF token is generated in a way that doesn't involve any information known only to the server. It's perfectly possible to generate a valid CSRF token in javascript - Django will happily accept it.
In particular, one could have a piece of javascript that generates valid CSRF token and sets it as a cookie (and it will work fine because of the same origin).
Are there any security related drawbacks of doing that? The only thing I can think of is that such cookie cannot have the http-only flag set (for obvious reasons).
The short answer is No
CSRF is a solution to restrict CSRF attacks. So in the server, a code is generated (and signed) to check is user using built-in js codes or not. for example, a user can call a function without CSRF protection in the browser console or with a browser extension or with curl without any browser, in this condition, you cannot understand is user knows this function call or not!
On the other side, you want to make a new CSRFTOKEN and send it to the server with ajax and this service cannot protect with CSRF. So the hackers can use this ajax call for CSRF forgery! And the hacker can find your CSRF maker code in your js libraries.
For your Idea, you can use some user info and save it in session info such as use-agent, client-IP, and ...
Related
I'm making a Django Rest Framework application with a JWT authentication with tokens stored in HttpOnly cookies. Authentication is performed via reading the access cookie. I'm using a dj-rest-auth library for this purpose, but I'm a little confused of the security aspect of such method. Knowing that having authentication data stored in cookies can be used to perform CSRF attack, how can one protect their web against such attacks for a specific case I've described above? All cookies are set to be SameSite=Lex.
Do I need to also send X-CSRFTOKEN header obtained from the backend? That would mean that every request to the api will need to have that header. What should be the optimal setup having all those libraries?
The OWASP foundation has created a detailed cheat-sheet on how to prevent from CSRF attacks: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
Two things worth remembering:
CSRF is a concern only for data-changing requests, so POST/PUT/PATCH/DELETE methods. An attacker performing a CSRF attack can't read the response from the server, so GET requests are not your concern.
The most common way of preventing CSRF is with a csrf token. The server should create the token for the user's session and the token is saved in frontend, but not in a cookie. Then you send that token either in a header or as part of the request body. The server then validates that the token is the same as the one it has for that user session.
If you don't want to persist the token in your server, you can additionally save it in a cookie. So the frontend will keep the token in two places - one in memory/in a hidden form field/etc. and another one in the cookie. Then the server can check if the value from the cookie matches the value from header/body.
All cookies are set to be SameSite=Lex.
Do I need to also send X-CSRFTOKEN header obtained from the backend?
SameSite is not supported by older browsers. As per caniuse, it is supported by 90% browsers in use globally.
Arguments AGAINST implementing CSRF tokens:
I think it's a low risk. If you think your users will be among those 90%, then don't bother with CSRF tokens.
Since you're using JWTs, I assume the frontend is going to be a modern SPA app. If you don't expect it to run on older browsers, then, again, don't bother with CSRF tokens.
Arguments FOR implementing CSRF tokens:
Lax offers no protection on GET requests. You'll have to make sure, today and in future as well, that you never modify server state in GET requests. If you implement CSRF tokens, you'll have one less thing to worry about. Or you can also use SameSite=Strict value.
My opinion:
Personally, I'd just implement CSRF tokens. It's a one time setup and most frameworks already take care of this. So, why not?
P.S.: I'm not sure if you've a typo there, but it is Lax, not Lex.
I'm building an application with django , Drf and currently using vanilla JS as Frontend for now.
I searched almost all through web on different use case for authentication on the web and I found out different links but these links seem to always favour the session authentication and token authentication.
Using Django helps us with the session authentication as default so I decided to study the auth process using a token auth.
While doing this, I initially used the localstorage as a store for my tokens gotten from the backend response after user authenticates, But for some reasons which are valid , most devs/engineers advise against using the localstorage as it prones one to xss attacks..
So I decided to implement the httponly cookie method, but I haven't seen a practical use of this done on Django, I've seen theories on implementing all these but haven't seen someone done this..
Please how can I use the httponly cookie with my token stored as a cookie with DJANGO
EDIT
I know a httponly cookie does not allow JavaScript to access a cookie, so I decided to do this.
Django sends the cookie httponly with the token as the cookie
User makes a request to the backend
server gets the token from the cookie sent as a request from the backend.
4)"where the problem now comes" I can't set the token as an header in Django, I tried using the request.headers['Autho...] = Token ....
But that doesn't allow item assignment..
So if my logic is correct this is where I'm stucked
EDIT So this time, I am now able to add a header from the server , using request.META to pass an Authorization key with the Token .... Value, that seems to work fine instead of having to use request.headers for passing an assignment..
But something happened which shocked me, in as much as I'm able to change or add an authorization token from the server , the view still gives me an error, much like I never passed a token at all.....
It's like after the whole efforts and everything nothing still changes, except if it's requested from the client side đ˘.
Guess I will have to stick with localstorage for now, but still research more or wait for answers .
I've done the Authentication with token using httponly cookie..
I recalled when I asked questions and some loving guys from here helped tho, we couldn't see a straight off answer as we had to research and think as well...
The steps I used was this.
Django takes in user credentials
Django authenticate that credentials
a token is exchanged for that data
we set the token to a cookie using
set_cookie(.... , httponly=True)
** Then it was now time for the real workout .
I created a middleware which will be responsible for setting the token to an Authorization key in header dict.. instead of allowing the client to do this.
---- The client couldn't handle this coz it was now a httponly flag which will prevent js from accessing it as the purpose of using httponly was for this to prevent xss attacks when tokens/cookies are normally stored in a browser storage
we then handle the middleware to our taste, as in mine I tried making sure it work for only some views and not all views (will be planning on making a custom decorator for it)
then last was to đ¤đ¤ we'll have fun and smile at seeing me create something as such without a previous tutorial...
The GitHub repo link https://github.com/HarryAustin/tweeter-DrfTest-httonlycookie
I am trying to enforce CSRF for a Django Rest API which is open to anonymous users.
For that matter, I've tried two different approaches:
Extending the selected API views from one CSRFAPIView base view, which has an #ensure_csrf_cookie annotation on the dispatch method.
Using a custom Authentication class based on SessionAuthentication, which applies enforce_csrf() regardless of whether the user is logged in or not.
In both approaches the CSRF check seems to work superficially. In case the CSRF token is missing from the cookie or in case the length of the token is incorrect, the endpoint returns a 403 - Forbidden.
However, if I edit the value of the CSRF token in the cookie, the request is accepted without issue. So I can use a random value for CSRF, as long as it's the correct length.
This behaviour seems to deviate from the regular Django login view, in which the contents of the CSRF do matter. I am testing in local setup with debug/test_environment flags on.
What could be the reason my custom CSRF checks in DRF are not validated in-depth?
Code fragment of the custom Authentication:
class RestCsrfAuthentication(SessionAuthentication):
def authenticate(self, request):
self.enforce_csrf(request)
rotate_token(request)
return None
And in settings:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'csrfexample.authentication.RestCsrfAuthentication',
]
}
The specific contents of CSRF tokens in Django never matter, actually.
This reply by a Django security team member to a question similar to yours says this:
The way our CSRF tokens work is pretty simple. Each form contains a CSRF token, which matches the CSRF cookie. Before we process the protected form, we make sure that the submitted token matches the cookie. This is a server-side check, but it's not validating against a stored server-side value. Since a remote attacker should not be able to read or set arbitrary cookies on your domain, this protects you.
Since we're just matching the cookie with the posted token, the data is not sensitive (in fact it's completely arbitrary - a cookie of "zzzz" works just fine), and so the rotation/expiration recommendations don't make any difference. If an attacker can read or set arbitrary cookies on your domain, all forms of cookie-based CSRF protection are broken, full stop.
(Actually "zzzz" won't work because of length requirements, but more on that later.) I recommend reading the entire mailing list message for a fuller understanding. There are explanations there about how Django is peculiar among frameworks because CSRF protections are independent of sessions.
I found that mailing list message via this FAQ item on the Django docs:
Is posting an arbitrary CSRF token pair (cookie and POST data) a vulnerability?
No, this is by design. Without a man-in-the-middle attack, there is no way for an attacker to send a CSRF token cookie to a victimâs browser, so a successful attack would need to obtain the victimâs browserâs cookie via XSS or similar, in which case an attacker usually doesnât need CSRF attacks.
Some security audit tools flag this as a problem but as mentioned before, an attacker cannot steal a userâs browserâs CSRF cookie. âStealingâ or modifying your own token using Firebug, Chrome dev tools, etc. isnât a vulnerability.
(Emphasis mine.)
The message is from 2011, but it's still valid, and to prove it let's look at the code. Both Django REST Framework's SessionAuthentication and the ensure_csrf_cookie decorator use core Django's CsrfViewMiddleware (source). In that middleware class's process_view() method, you'll see that it fetches the CSRF cookie (a cookie named csrftoken by default), and then the posted CSRF token (part of the POSTed data, with a fallback to reading the X-CSRFToken header). After that, it runs _sanitize_token() on the POSTed/X-CSRFToken value. This sanitization step is where the check for the correct token length happens; this is why you're getting 403s as expected when you provide shorter or longer tokens.
After that, the method proceeds to compare the two values using the function _compare_salted_tokens(). If you read that function, and all the further calls that it makes, you'll see that it boils down to checking if the two strings match, basically without regard to the values of the strings.
This behaviour seems to deviate from the regular Django login view, in which the contents of the CSRF do matter.
No, it doesn't matter even in the built-in login views. I ran this curl command (Windows cmd format) against a mostly default Django project:
curl -v
-H "Cookie: csrftoken=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
-H "X-CSRFToken: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
-F "username=admin" -F "password=1234" http://localhost:8000/admin/login/
and Django returned a session cookie (plus a CSRF cookie, of course).
Just a note on the way you're overriding SessionAuthentication.authenticate(): you probably already know this, but according to the DRF docs that method should return a (User, auth) tuple instead of None if the request has session data, i.e. if the request is from a logged-in user. Also, I think rotate_token() is unnecessary, because this code only checks for authentication status, and is not concerned with actually authenticating users. (The Django source says rotate_token() âshould be done on loginâ.)
Is it disabling CSRF functionality the best practice for Slack slash command server?
I want to call an API view function in Django by a Slack slash command, for example, /test.
When I call the URL for the view function with any browser (so it is a GET request), it works as expected.
However, when I run /test in Slack, I got 403_client_error in slack, and Forbidden (CSRF cookie not set) in the Django shell.
I believe this is because Slack sends a POST request, and Django requires CSRF token for any POST requests.
My question is whether I should disable CSRF checking for this view. Will there be a significant risk? Or is there any workaround?
Short Answer:
Yes, it's fine for an API endpoint.
Longer Answer:
Cross-Site Request Forgery (CSRF) Tokens are a means of preventing CSRF Attacks. These attacks basically work by placing some malicious code on another site that runs when the user visits that site. Upon loading the malicious site, some JavaScript code runs and submits a POST request to a site that the victim is already authenticated at. Since the user is already authenticated, the server for the other siteâa bank, for exampleâthinks that the user is actually making this requestâsend the attacker $100, for exampleâand goes ahead and processes the request. Here is a really good description of CSRF Attacks and Tokens: https://stackoverflow.com/a/33829607/8068625
The way that CSRF Tokens prevent this is by putting a token somewhere on any page that contains a form and that token gets submitted as an additional field on the form when submitted. This way the page must actually be visited (which the attacker should not be able to accomplish) in order to submit the page.
So, as long as this page you are removing the CSRF protection from does not have a form on it that you would not like submitted by an attacker, it is fine to add #csrf_exempt to the view. It seems that this is the case since the endpoint exists solely as an API endpoint for a Slack Slash Command.
You might consider adding some other form of authentication for your API endpoints (note that CSRF is not for use in APIs) like, for example, JSON Web Tokens. But that is up to you.
There are quite a lot written about preventing CSRF.
But I just don't get it: why I can't just parse the csrf token in the target page form and submit it with my forge request?
If you are able to inject script code into the target page (XSS) then yes, you can do that thus rendering the CSRF prevention useless.
The CSRF token has to be stored in the page the end-user receives (or he won't know it either).
In fact, in security assessments, XSS usually evaluated not for its own damage potential but for its use in just such attacks.
CSRF attacks are blind. They do session riding and the attacker has no direct control unless he can extract the token via an XSS vulnerability. Normally a session wide token can be used. Rotating tokens per request might be an overkill and could lead to false alarms. I prefer to use tokens per resource with a master session token.
The CSRF token should be everytime for every user and for every request a totally different token, so it can never be guessed from an attacker.
For php, .net and javascript have a look in the OWASP CSRFGuard Project - if you are working with java and jsf 2.x its already save against CSRF (as long as you use POST and not GET - for this you will have to wait for JSF 2.2) else if you work without JSF the HTTPUtillities Interface from the OWASP ESAPI could be also very helpful!
Because the value of the CSRF token isn't known in advance.