how to disable csrf in testing django? - django

I have a problem testing views with csrf tokens.
This code
class ViewTests(TestCase):
def test_bets_view(self):
login_page = self.client.get('/users/login/')
print(login_page.content)
returns HTML with CSRF hidden input.
And this thing, which I need to compare to the former HTML,
expected_html = render_to_response('login.html',
dictionary={'form': LoginForm()})
doesn't have hidden CSRF input. So the assertion fails.
Ho do I disable CSRF rendering in test client?

You can override your middleware settings in your unit test like this:
from django.test import override_settings
testing_middleware = [ ... anything but csrf middleware goes here ]
class TestWhatever(TestCase)
#override_settings(MIDDLEWARE=testing_middleware)
def testSomething():
# no csrf error will occur
self.client.post('/', data={ ... })

First you get the page in your tests and extract the csrf token from it:
page = self.client.get("/users/login")
token = page.context.get("csrf_token")
Then you use the same token to render the template and compare:
expected_html = TemplateResponse(
page.wsgi_request,
"login.html",
context={"form": LoginForm(), "csrf_token": token}).render()
assert expected_html.content == page.content

You should never compare the complete HTML content. Just check the functionalities. In case you need disabling the csrf at any cost, following logic should help I guess.
In your views.py file, add the following package
from django.views.decorators.csrf import csrf_exempt
Then just before the function definintion, in which you are performing your checks, add this snippet:
#csrf_exempt
This will disable the default verification of csrf. Even if your incoming request has a hidden csrf token, your server function will completely ignore it. This should do the trick of disabling the csrf.

Related

Django DRF - Manually validate CSRF token

I read in the docs that DRF only validates CSRF tokens on authenticated requests and login views should explicitely check the CSRF token.
Problem is, how does one manually check the CSRF token?
In my settings.py:
MIDDLEWARE = [
...
"django.middleware.csrf.CsrfViewMiddleware",
...
]
Here's my view:
from rest_framework.decorators import api_view
from django.http import JsonResponse
from django.views.decorators.csrf import get_token
# I have to manually generate the csrf token and put it in the response body, because I use react-native and that can't read the token from the 'Set-Cookie' header
#api_view(["GET"])
def user_info(request):
return JsonResponse({"csrf_token": get_token(request)})
#api_view(["POST"])
def login(request):
return JsonResponse({"foo": "bar"})
When I make a POST request in the frontend and I don't provide the CSRF token, it should fail, but I actually receive the {"foo": "bar"} JSON.
I tried the #csrf_protect and #requires_csrf_token decorators, but the request is still not failing.
I also tried this, but that errors on required positional argument: 'get_response
CsrfViewMiddleware().process_view(request, None, (), {})
If I pass a function to get_response, that function is never called:
def test_get_response(req):
breakpoint()
CsrfViewMiddleware(get_response=test_get_response).process_request(request)
I checked that the CSRF token is not passed in the headers, not the cookies, and I tried different browsers and incognito mode.
How do I get my api requests to fail if they don't include a valid CSRF token?
You're having error in this importing from django.views.decorators.csrf import get_token . you should import get_token from django.middleware.csrf .
according to django documentation and it return the CSRF token required for a POST form.
Okay... a few rabbit holes later, I found some kind of solution.
Django uses Double Submit Cookie for CSRF validation. This means:
Frontend requests CSRF token from Django
Django generates token, sends it to FE in Set-Cookie header
FE puts csrf in cookie, which cannot be altered by a potential attacker
FE puts the same token in POST form and sends it to Django
Django checks if token in POST form and cookie are the same
Problem is, React Native doesn't support cookies, so it has to send the CSRF token some other way. Problem with that is there is not way to do that securely.
I solved it by writing custom middleware for django:
FE requests CSRF token from Django
Django generates token, sends it to FE, AND stores hashed version in db
FE puts it in POST form and sends it to Django
Django hashes the token, checks if hash is in db
Upon successful validation, Django removes token from db
A CRON job removes any unused tokens after 24 hours
Bit cumbersome, but I only have to validate this way on web requests.

csrf_exempt set but CSRF Failed: Referer checking failed - no Referer

I have a backend API, it's in django and deployed on Google Endpoint.
I have a post request that insert data to my DB.
I created a script to use this endpoint but I got this error:
{"detail":"CSRF Failed: Referer checking failed - no Referer."}
Regarding over posts I added the crsf_exempt decorator to my class but it did not change.
I try to add the decorator two ways:
class AddUser(APIView):
""" Create user and company from csv """
#method_decorator(csrf_exempt)
def post(self, request):
#method_decorator(csrf_exempt, name='dispatch')
class AddUser(APIView):
""" Create user and company from csv """
def post(self, request):
But both failed.
This is how I contact my endpoint:
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
open_id_connect_token)}, **kwargs)
Any ideas ?
Thanks
EDIT
So I tried to add authentication classes to my views but it appears to be a bad idea. This is being real trouble for me.
I tried to get the csrftoken doing like this:
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
print(client.cookies)
if 'csrftoken' in client.cookies:
# Django 1.6 and up
csrftoken = client.cookies['csrftoken']
else:
# older versions
csrftoken = client.cookies
Thing is, I am using IAP to protect my API and I do not have any csrftoken cookie but I do have a something looking like this:
<RequestsCookieJar[<Cookie GCP_IAP_XSRF_NONCE_Q0sNuY-M83380ypJogZscg=1
for ...
How can I use this to make post request to my API ?
So this happened to me because I did not set any authentication_classes to my generic view.
When this option is not set Django automatically use the SessionBackend, which need the csrf token.
I fixed it by adding this to my view: authentication_classes = [ModelBackend, GoogleOAuth2]
#Kimor - Can you try doing this in your urls.py
from django.views.decorators.csrf import csrf_exempt
url('^test/$', csrf_exempt(views.TestView.as_view())),
The get and post methods defined on the APIView class just tell DRF how the actual view should behave, but the view method that the Django router expects is not actually instantiated until you call TestView.as_view().
source
Django REST Framework CSRF Failed: CSRF cookie not set
So after working on this project for a while this is what I learned regarding the CSRF using Django.
First of all, if you are using django templates, or in any cases where your back-end and front-end are running behind the same server the most common practice is to use session for authentication.
This is activated by default by DRF.
This means that in your DRF configuration if you do not explicitly set the DEFAULT_AUTHENTICATION_CLASSES option default authentication will be set to Session + BasicAuth.
In this configuration you'll need to manage the CSRF token as described in the documentation (https://docs.djangoproject.com/en/4.0/ref/csrf/).
If your back-end and front-end are separated as in my case, using CSRF is not the only solution or even the recommended one.
As in my case I use JWT behind IAP (Identity Aware Proxy, provided by google). I had to write my own authentication classes and then use it in my DRF conf:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'main.authentication_backend.custom_auth.IAPAuthentication'],
...
}
Here is explain how to write your own authentication class: https://www.django-rest-framework.org/api-guide/authentication/#custom-authentication.

Django Rest Framework remove csrf

I know that there are answers regarding Django Rest Framework, but I couldn't find a solution to my problem.
I have an application which has authentication and some functionality.
I added a new app to it, which uses Django Rest Framework. I want to use the library only in this app. Also I want to make POST request, and I always receive this response:
{
"detail": "CSRF Failed: CSRF token missing or incorrect."
}
I have the following code:
# urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns(
'api.views',
url(r'^object/$', views.Object.as_view()),
)
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
class Object(APIView):
#csrf_exempt
def post(self, request, format=None):
return Response({'received data': request.data})
I want add the API without affecting the current application.
So my questions is how can I disable CSRF only for this app ?
Note: Disabling CSRF is unsafe from security point of view. Please use your judgement to use the below method.
Why this error is happening?
This is happening because of the default SessionAuthentication scheme used by DRF. DRF's SessionAuthentication uses Django's session framework for authentication which requires CSRF to be checked.
When you don't define any authentication_classes in your view/viewset, DRF uses this authentication classes as the default.
'DEFAULT_AUTHENTICATION_CLASSES'= (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
Since DRF needs to support both session and non-session based authentication to the same views, it enforces CSRF check for only authenticated users. This means that only authenticated requests require CSRF tokens and anonymous requests may be sent without CSRF tokens.
If you're using an AJAX style API with SessionAuthentication, you'll need to include a valid CSRF token for any "unsafe" HTTP method calls, such as PUT, PATCH, POST or DELETE requests.
What to do then?
Now to disable csrf check, you can create a custom authentication class CsrfExemptSessionAuthentication which extends from the default SessionAuthentication class. In this authentication class, we will override the enforce_csrf() check which was happening inside the actual SessionAuthentication.
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
In your view, then you can define the authentication_classes to be:
authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)
This should handle the csrf error.
Easier solution:
In views.py, use django-braces' CsrfExemptMixin and authentication_classes:
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from braces.views import CsrfExemptMixin
class Object(CsrfExemptMixin, APIView):
authentication_classes = []
def post(self, request, format=None):
return Response({'received data': request.data})
Modify urls.py
If you manage your routes in urls.py, you can wrap your desired routes with csrf_exempt() to exclude them from the CSRF verification middleware.
import views
from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt
urlpatterns = patterns('',
url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
...
)
Alternatively, as a Decorator
Some may find the use of the #csrf_exempt decorator more suitable for their needs
for instance,
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
should get the Job Done!
For all who did not find a helpful answer. Yes DRF automatically removes CSRF protection if you do not use SessionAuthentication AUTHENTICATION CLASS, for example, many developers use only JWT:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
But issue CSRF not set may be occurred from some another reason, for exmple you not correctly added path to you view:
url(r'^api/signup/', CreateUserView), # <= error! DRF cant remove CSRF because it is not as_view that does it!
instead of
url(r'^api/signup/', CreateUserView.as_view()),
I tried a few of the answers above and felt creating a separate class was a little overboard.
For reference, I ran into this problem when trying to update a function based view method to a class based view method for user registration.
When using class-based-views (CBVs) and Django Rest Framework (DRF), Inherit from the ApiView class and set permission_classes and authentication_classes to an empty tuple. Find an example below.
class UserRegistrationView(APIView):
permission_classes = ()
authentication_classes = ()
def post(self, request, *args, **kwargs):
# rest of your code here
If you do not want to use session based authentication, you can remove Session Authentication from REST_AUTHENTICATION_CLASSES and that would automatically remove all csrf based issues. But in that case Browseable apis might not work.
Besides this error should not come even with session authentication. You should use custom authentication like TokenAuthentication for your apis and make sure to send Accept:application/json and Content-Type:application/json(provided you are using json) in your requests along with authentication token.
You need to add this to prevent default session authentication: (settings.py)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
Then: (views.py)
from rest_framework.permissions import AllowAny
class Abc(APIView):
permission_classes = (AllowAny,)
def ...():
You need to be absolutely sure, that you want to switch off CSRF protection.
Create file authentication.py and place it wherever you want in your project. For example, in folder session_utils.
Place this code in the file:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
pass
When you want to make POST, PUT, PATCH or DELETE requests to your view be sure that you've changed SessionAuthentication to SessionCsrfExemptAuthentication from the new file. View example:
#api_view(["POST"])
#authentication_classes([SessionCsrfExemptAuthentication])
#permission_classes([IsAuthenticated])
def some_view(request) -> "Response":
# some logic here
return Response({})
This trick allow you to override method (pass) enforce_csrf and the new session authentication class will skip CSRF check.
✌️
I am struck with the same problem. I followed this reference and it worked.
Solution is to create a middleware
Add disable.py file in one of your apps (in my case it is 'myapp')
class DisableCSRF(object):
def process_request(self, request):
setattr(request, '_dont_enforce_csrf_checks', True)
And add the middileware to the MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
My Solution is shown blow. Just decorate my class.
from django.views.decorators.csrf import csrf_exempt
#method_decorator(csrf_exempt, name='dispatch')
#method_decorator(basic_auth_required(
target_test=lambda request: not request.user.is_authenticated
), name='dispatch')
class GenPedigreeView(View):
pass
When using REST API POSTs, absence of X-CSRFToken request header may cause that error.
Django docs provide a sample code on getting and setting the CSRF token value from JS.
As pointed in answers above, CSRF check happens when the SessionAuthentication is used. Another approach is to use TokenAuthentication, but keep in mind that it should be placed first in the list of DEFAULT_AUTHENTICATION_CLASSES of REST_FRAMEWORK setting.
If you are using an exclusive virtual environment for your application, you can use the following approach without effective any other applications.
What you observed happens because rest_framework/authentication.py has this code in the authenticate method of SessionAuthentication class:
self.enforce_csrf(request)
You can modify the Request class to have a property called csrf_exempt and initialize it inside your respective View class to True if you do not want CSRF checks. For example:
Next, modify the above code as follows:
if not request.csrf_exempt:
self.enforce_csrf(request)
There are some related changes you'd have to do it in the Request class
This could also be a problem during a DNS Rebinding attack.
In between DNS changes, this can also be a factor. Waiting till DNS is fully flushed will resolve this if it was working before DNS problems/changes.
For me, using django 3.1.5 and django rest framework 3.12 the solution was way easier.
It happened to me that on a views.py file I had defined this two methods:
#api_view(['POST'])
#permission_classes((IsAuthenticated, ))
def create_transaction(request):
return Response(status=status.HTTP_200_OK)
def create_transaction(initial_data):
pass
On my urls.py:
urlpatterns = [
path('transaction', views.create_transaction, name='transaction'),
]
Django was picking the latest and throwing the error. Renaming one of the two solved the issue.
Code bellow would remove demand for CSRF. Even anon user would be able to send request.
from typing import List, Any
class Object(APIView):
authentication_classes: List = []
permission_classes: List[Any] = [AllowAny]
...
...
Removing CSRF check is not always the only (or best) solution. Actually, it's an important security mechanism for SessionAuthentication.
I was having the same issue when trying to authenticate with JWT and doing a POST request.
My initial setup looked like this:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.SessionAuthentication",
"django_cognito_jwt.JSONWebTokenAuthentication",
),
...
}
As SessionAuthentication was checked first in the list, the CSRF error was raised. My solution was as simple as changing the order to always check JWT auth first. Like this:
"DEFAULT_AUTHENTICATION_CLASSES": (
"django_cognito_jwt.JSONWebTokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
At the end, SessionAuthentication for me is only used in the django admin panel and 99% of the requests goes to the API that uses JWT auth.

Django check CSRF token manually

I am implementing an API that works either with an API key, or with a CSRF token. The goal is for it to be usable either by a web app (protected by CSRF) or by a third party application (protected by API key).
Basically on each request (all via POST), I check if there is an API key. If there is a valid one, it's good to go. If not, I want to fall back to verifying CSRF.
Is there a function I can call to verify the CSRF myself? The view itself is #csrf_exempt because API keys need to work.
You could probably subclass the CsrfViewMiddleware class and override the process_view method. Then include your custom middleware instead of the default CSRF one.
from django.middleware.csrf import CsrfViewMiddleware
class CustomCsrfMiddleware(CsrfViewMiddleware):
def process_view(self, request, callback, callback_args, callback_kwargs):
if request.META.get('api_key'):
# process api key
else:
return super(CsrfViewMiddleware, self).process_view(...)
You can use builtin csrf verification like this:
from django.middleware.csrf import CsrfViewMiddleware
def check_csrf(request):
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason:
# CSRF failed
raise PermissionException() # do what you need to do here
I've been accessing the CsrfViewMiddleware like Aldarund has shown but more needs to be said about this kind of solution:
If you are performing the test in a view, then you can return reason directly. According to how Django middleware works, when process_view returns something else than None, then it must be a HttpResponse object so it can just be returned by the view.
There may be cases where you do not want to return reason directly, but if there is no reason not to do so, I'd rather return it, for consistency with how the site behaves in other cases.
If you use the test in a run-of-the-mill view, and you already use CsrfViewMiddleware site-wide, it is usually the case that request will already have passed once through CsrfViewMiddleware. (Yes, it can happen. I have a case where a request I receive is modified and retested through CsrfViewMiddleWare after it has already been tested by CsrfViewMiddleware due to the site-wide configuration.) However, the middleware sets csrf_processing_done on request after it tests it and won't retest it if it is called again, because of this flag. So csrf_processing_done has to be reset to False to perform a second test.
Here's an illustration of the above:
from django.middleware.csrf import CsrfViewMiddleware
def view(request):
request.csrf_processing_done = False
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason is not None:
return reason # Failed the test, stop here.
# process the request...
In my case, I wanted to POST some raw data with CSRF check.
So, I use this decorator requires_csrf_token in the view which process POST data :
from django.views.decorators.csrf import requires_csrf_token
#requires_csrf_token
def manage_trade_allocation_update(request):
In my template, I added csrf_token génération and put it in data post :
{% csrf_token %}
...
data['csrfmiddlewaretoken'] = document.querySelector('input[name="csrfmiddlewaretoken"]').value;
With this mecanism, I can use CSRF protection with manual HTTP POST request.

How to convert a Django HttpResponse to a Django render call

I have the following code
def ajax_login_request(request):
try:
request.POST[u'login']
dictionary = request.POST
except:
dictionary = request.GET
user = authenticate(username = dictionary[u'login'], password = dictionary[u'password'])
if user and user.is_active:
login(request, user)
result = True
else:
result = False
response = HttpResponse(json.dumps(result), mimetype = u'application/json')
return response
which is being called via ajax. I'm a noob and this is from an example in a book. Unfortunately the version of Django I'm using throws a CSRF error on this. I've done the other CSRF bits, but I don't know how to change the HttpResponse bit to a render call. I do not want to use CSRF_exempt, because I have no idea when that is appropriate. Can someone please provide me the equivalent render call for the HttpResponse above.
Thanks
To make your original code work, you need to get a RequestContext object and pass it along with your response, something like this:
from django.http import HttpResponse
from django.template import RequestContext, Template
def ajax_login_request(request):
# ...
# This bit of code adds the CSRF bits to your request.
c = RequestContext(request,{'result':json.dumps(result)})
t = Template("{{result}}") # A dummy template
response = HttpResponse(t.render(c), mimetype = u'application/json')
return response
Do read up on the CSRF documentation as you might run into strange errors if you don't understand all the ways CSRF is "wired" in your app. The page also has a javascript snippet to make sure CSRF cookies are sent with your ajax requests if you are sending them without a form.
You can also use the render_to_response() shortcut, but you would need to have an actual template to load (in your case, you don't need a template, hence the "dummy" template in my example).
Ok, I'm going to re-draft this answer so you understand where I'm coming from. The CSRF middleware works like this:
You make request -------> request hits csrf --(invalid/no token)--> render 403
middleware
|
(valid token)
|
\ /
Call view
|
\ /
middleware sets
csrf cookie
|
\ /
Response appears
In other words, if you are seeing a 403 csrf page, your view was never called. You can confirm this by sticking a spurious print statement in the view and watching the output from runserver when you make your request.
To solve this, you either need to disable csrf (not good) or use one of the ajax methods available to you. If the required token is passed in your view will actually be executed.
The reason your view is not called is to prevent the action from the forged site from actually ever taking place - for example, if you denied the template at response time the user would already be logged in. The same behaviour occurs with the function decorators.
As for the middleware setting the cookie, that does not alter or depend on the render function at all - this sets the HTTP header Cookie: ... in the response. All responses in Django are HttpResponse objects until it finally converts them to output; render functions are helpers, but that's not what's causing your problem here.
Edit I'll convert what you've got to a render call. You could do this:
return render_to_response(`ajax_templates/login_response.html`,
{'loginresponse': json.dumps(result)})
Where ajax_templates/login_response.html is just:
{% loginresponse %}
That's it. HttpResponse has a main default argument which is the string to return (literally, the html of the web page); that's what you're doing initially. render_to_response and render are shortcuts to this which do this:
render_to_response called ----> open template asked for --> substitute arguments
|
\ /
django instructs web server <--- return this from view <-- create HttpResponse
to send to client object