CSRF protection in Django - django

I don't get one thing about CSRF protection in django. For example we have some malicious site. What is the problem to send get-request from this site to csrf protected url, parse the page and get csrf value, then post with this value?

For example we have some malicious site. What is the problem to send
get-request from this site to csrf protected url, parse the page and
get csrf value, then post with this value?
If you do this, the session counterpart of the CSRF cookie will not match, and your request will be rejected.
Also, it should be noted that referrer check is done only for HTTPS requests to prevent a MitM vulnerability.
See this django wiki entry for a discussion on how CSRF protection works, and this SO question that discusses the MitM attack specifically.

The main purpose of Django's CSRF is explained in the Django Docs (https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-it-works):
This ensures that only forms that have originated from your Web site
can be used to POST data back.
So it checks several things - cookie, referrer, posted value. And there are some limitations, that you cannot always modify all these values at your will. For example - you can set X-CSRFToken token and the POST value in an AJAX call, but the browser will not allow you to override the referrer header... You might succeed to do a successful POST using urllib2 or similar library, but this is not covered by the CSRF protection, as it is the same as you POST on a page.
Again - CSRF means Cross Site Request Forgery and it is what it protects.

REFERRER will be checked. If the REFERRER does correspond to correct URL then POSTing data is not valid.

Related

Why does Django/Django REST Framework not validate CSRF tokens in-depth, even with enforce-CSRF?

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”.)

What it the meaning of csrf token?

I am getting this error please haldle the csrf token in django project.When I first made an AJAX call with a POST request, I got a HTTP 403 Forbidden error. A quick debug led me to the CSRF authorisation problem. The backend refused to authorise the request because there is no accompanying CSRF token to prove that the request is not from a foreign site.
From the documentation:
"The CSRF middleware and template tag provides easy-to-use
protection against Cross Site Request Forgeries. This type of attack occurs when a malicious website contains a link, a form button
or some JavaScript that is intended to perform some action on your
website, using the credentials of a logged-in user who visits the
malicious site in their browser."
Therefore, when making a POST request, you should always include a CSRF token.
For more information, including how to use it with AJAX calls, please refer to the documentation:
https://docs.djangoproject.com/en/3.0/ref/csrf/

How to use CSRF security for a backend django for an mobile application client

I'm developing a backend for a mobile app with Django where for user registration the user data are sent with the POST method. Since Django provide CSRF security as a middleware. Here my problem is if I have a front end I can enable CSRF token by jinja code as {% csrf_token %} but since it's a backend and how to resolve this problem
To guard against these type of attacks, you need to do two things:
1 Ensure that the 'safe' HTTP operations, such as GET, HEAD and OPTIONS cannot be used to alter any server-side state.
2 Ensure that any 'unsafe' HTTP operations, such as POST, PUT, PATCH and DELETE, always require a valid CSRF token.
If you're using SessionAuthentication you'll need to include valid CSRF tokens for any POST, PUT, PATCH or DELETE operations.
In order to make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation.
Check this link. It explains exactly what your approach should be.
Basically you need to send csrf token in the header if you want the POST request to have csrf. But we do not ask for csrf tokens for non authenticated requests.
It would be better if you go with Django Rest Framework to create APIs if you aren’t already using it
One solution is to remove csrf token line form setting.py file :)

Is it okay to disable CSRF for slack slash command api mad in Django?

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.

Is it a security risk to include 'CSRF token' in pages requiring no authentication?

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.