Django Rest Framework移除CSRF


111

我知道有关于Django Rest Framework的答案,但是我找不到解决问题的方法。

我有一个具有身份验证和某些功能的应用程序。我向其中添加了一个新应用程序,该应用程序使用Django Rest Framework。我只想在此应用程序中使用库。我也想发出POST请求,并且总是收到以下响应:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

我有以下代码:

# 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})

我想添加API而不影响当前应用程序。因此,我的问题是如何仅对此应用程序禁用CSRF?


您已经在使用@csrf_exempt令牌。您可以在整个视图中使用它。那不行吗
mukesh 2015年

不,我仍然得到详细信息:“ CSRF失败:CSRF令牌丢失或不正确。” 信息。我从答案中得出结论,我应该删除默认身份验证。
艾琳·德州

1
使用令牌认证时,我遇到了非常相似的情况。对于在同一条船上其他人:stackoverflow.com/questions/34789301/...
酒仙

Answers:


218

为什么会发生此错误?

发生这种情况是由于SessionAuthenticationDRF使用默认方案。DRF SessionAuthentication使用Django的会话框架进行身份验证,这需要检查CSRF。

当您authentication_classes在视图/视图集中未定义任何对象时,DRF将此身份验证类用作默认身份验证类。

'DEFAULT_AUTHENTICATION_CLASSES'= (
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication'
),

由于DRF需要支持对同一视图的基于会话的身份验证和基于非会话的身份验证,因此DRF仅对经过身份验证的用户实施CSRF检查。这意味着只有经过身份验证的请求才需要CSRF令牌,并且匿名请求可以在没有CSRF令牌的情况下发送。

如果您将AJAX样式的API与SessionAuthentication一起使用,则需要为任何“不安全的” HTTP方法调用(例如PUT, PATCH, POST or DELETE请求)包括有效的CSRF令牌。

那该怎么办?

现在要禁用csrf检查,您可以创建CsrfExemptSessionAuthentication从默认SessionAuthentication类扩展的自定义身份验证类。在此身份验证类中,我们将覆盖enforce_csrf()在实际内部进行的检查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

在您看来,然后可以将定义authentication_classes为:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

这应该处理csrf错误。


10
抱歉,我可能没把握重点,但是绕过/禁用csrf保护不是安全隐患吗?
Paolo

1
@Paolo OP需要禁用特定API的CSRF身份验证。但是,是的,禁用csrf保护具有安全风险。如果需要针对特定​​用例禁用会话身份验证,则他可以使用此解决方案。
拉胡尔·古普塔

嘿@RahulGupta-没有办法检查视图上的csrf_exempt装饰器,然后仅禁用那些视图的force_csrf吗?
Abhishek

@Abhishek也许您正在寻找bixente57 的以下ans。它为自定义视图禁用csrf。
Rahul Gupta '18

1
@RahulGupta如果您不想执行force_csrf,那么最好的方法是什么?
游戏玩家

21

更简单的解决方案:

在views.py中,使用花括号CsrfExemptMixin和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})

1
谢谢,这是解决该问题的最简单方法。我的API使用oauth2_provider和令牌。
Dat TT

1
啊,老兄。我有CsrfExemptMixin,但没有authentication_classes = []。谢谢!
MagicLAMP '17

仅供参考,authentication_classes行似乎是关键。无论有没有CsrfExemptMixin,对我来说都是一样的。
Dashdrum

14

修改urls.py

如果您在urls.py中管理路由,则可以使用csrf_exempt()包装所需的路由,以将其从CSRF验证中间件中排除。

from django.conf.urls import patterns, url
    from django.views.decorators.csrf import csrf_exempt
    import views

urlpatterns = patterns('',
    url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
    ...
)

另外,作为装饰者,有些人可能会发现使用@csrf_exempt装饰者更适合他们的需求

例如,

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

应该完成工作!


对代码的一些解释将为您提供更好的答案。
chevybow

@chevybow真的很抱歉,我实际上是社区的新手。实际上,它是Django的装饰器,用于针对特定视图禁用CSRF
Syed Faizan

这对我和python3和django 1.11一起工作,似乎最简单!
madannes '18

12

对于所有找不到有用答案的人。是的,如果您不使用SessionAuthenticationAUTHENTICATION CLASS,DRF会自动删除CSRF保护,例如,许多开发人员仅使用JWT:

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),

但是问题CSRF not set可能是由其他原因引起的,例如,您未正确添加视图路径:

url(r'^api/signup/', CreateUserView),  # <= error! DRF cant remove CSRF because it is not as_view that does it!

代替

url(r'^api/signup/', CreateUserView.as_view()),

8

我尝试了上面的一些答案,并觉得创建一个单独的类有点麻烦。

作为参考,当尝试将基于函数的视图方法更新为用于用户注册的基于类的视图方法时,我遇到了此问题。

当使用基于类的视图(CBV)和Django Rest Framework(DRF)时,从ApiView类继承,并将Permission_classes和authentication_classes设置为一个空元组。在下面找到一个例子。

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

    def post(self, request, *args, **kwargs):

        # rest of your code here

7

如果您不想使用基于会话的身份验证,则可以Session Authentication从REST_AUTHENTICATION_CLASSES中删除,这将自动删除所有基于csrf的问题。但是在这种情况下,可浏览api可能无法正常工作。

此外,即使会话身份验证也不应出现此错误。您应该对api使用自定义身份验证(例如TokenAuthentication),并确保在请求中发送Accept:application/jsonContent-Type:application/json(假设您使用的是json)以及身份验证令牌。


4

您需要添加它以防止默认会话身份验证:(settings.py)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', 
    )
}

然后:(views.py)

from rest_framework.permissions import AllowAny

class Abc(APIView):
    permission_classes = (AllowAny,)

    def ...():

3

我也遇到同样的问题。我遵循此参考,它起作用了。解决方案是创建一个中间件

在您的一个应用程序中添加disable.py文件(在我的情况下为“ myapp”)

class DisableCSRF(object):
    def process_request(self, request):
        setattr(request, '_dont_enforce_csrf_checks', True)

并将中间件添加到MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

4
这会使您的整个网站容易受到CSRF攻击。en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno

1

如果您为应用程序使用专有虚拟环境,则可以使用以下方法,而不会使任何其他应用程序生效。

您观察到的事情发生是因为rest_framework/authentication.py在类的authenticate方法中包含以下代码SessionAuthentication

self.enforce_csrf(request)

您可以修改Request该类以具有一个称为的属性csrf_exempt,并在各自的View类中对其进行初始化,以True实现不希望进行CSRF检查的目的。例如:

接下来,修改上面的代码,如下所示:

if not request.csrf_exempt:
    self.enforce_csrf(request)

您需要在Request课堂上进行一些相关的更改。此处提供完整的实现(具有完整描述):https : //github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed


1

我的解决方案显示为打击。只是装饰我的课。

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

1
尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以改善其长期价值。
Alex Riabov

0

使用REST API POST时,缺少X-CSRFToken请求标头可能会导致该错误。 Django文档提供了有关从JS获取和设置CSRF令牌值的示例代码。

正如上面的答案所指出的,使用SessionAuthentication时会发生CSRF检查。另一种方法是使用TokenAuthentication,但请记住,应将其放在REST_FRAMEWORK设置的DEFAULT_AUTHENTICATION_CLASSES列表的第一位。


-1

这在DNS重新绑定攻击期间也可能是一个问题。

在DNS更改之间,这也可能是一个因素。如果在DNS问题/更改之前它一直在工作,则等到DNS完全刷新后才能解决此问题。


这与上面的问题有什么关系?
boatcoder '19

这意味着当您切换DNS并且未完全传播时,可能会出现此问题。如果应用程序的路由与Django常规会话的路由不同,这就是原因。只是告知我遇到的一个极端情况。这似乎是一个规范的资源,所以我想我将添加一个额外的资源。
克里斯·弗里西纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.