Django Rest Framework 删除 csrf

Django Rest Framework remove csrf

我知道有关于 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?

如果您不想使用基于会话的身份验证,您可以从 REST_AUTHENTICATION_CLASSES 中删除 Session Authentication,这样会自动删除所有基于 csrf 的问题。但在那种情况下,Browseable api 可能不起作用。

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

注意:从安全角度来看,禁用 CSRF 是不安全的。请根据您的判断使用以下方法。

为什么会出现这个错误?

这是因为 DRF 使用的默认 SessionAuthentication 方案。 DRF 的SessionAuthentication 使用 Django 的会话框架进行身份验证,需要检查 CSRF。

当您未在 view/viewset 中定义任何 authentication_classes 时,DRF 使用此身份验证 classes 作为默认值。

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

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

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

那怎么办?

现在要禁用 csrf 检查,您可以创建一个从默认 SessionAuthentication class 扩展的自定义身份验证 class CsrfExemptSessionAuthentication。在此身份验证 class 中,我们将覆盖在实际 SessionAuthentication.

中发生的 enforce_csrf() 检查
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 错误。

我遇到了同样的问题。我遵循了这个 reference 并且它起作用了。 解决方案是创建一个中间件

在您的其中一个应用程序中添加 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,
)

更简单的解决方案:

在 views.py 中,使用 django-braces' CsrfExemptMixinauthentication_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})

如果您正在为您的应用程序使用独占虚拟环境,您可以使用以下方法而不影响任何其他应用程序。

您观察到的情况发生是因为 rest_framework/authentication.pySessionAuthenticationauthenticate 方法中有此代码 class:

self.enforce_csrf(request)

您可以修改 Request class 以拥有一个名为 csrf_exempt 的 属性 并在您各自的视图 class 中将其初始化为 True 如果您不想进行 CSRF 检查。例如:

接下来将上面的代码修改如下:

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

您必须在 Request class

中进行一些相关更改

对于所有没有找到有用答案的人。是的,如果你不使用 SessionAuthentication AUTHENTICATION 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()),

我的解决方案如下所示。装饰一下我的 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

修改urls.py

如果您在 urls.py 中管理您的路由,您可以用 csrf_exempt() 包装您想要的路由,以将它们从 CSRF 验证中间件中排除。

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())),
    ...
)

或者,作为装饰者 有些人可能会发现使用 @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')

应该完成工作!

这也可能是 DNS Rebinding attack 期间的一个问题。

在 DNS 更改之间,这也可能是一个因素。如果它在 DNS problems/changes.

之前工作,请等待 DNS 完全刷新将解决此问题

我尝试了上面的一些答案,觉得创建一个单独的 class 有点过分了。

作为参考,我 运行 在尝试将基于函数的视图方法更新为基于 class 的用户注册视图方法时遇到了这个问题。

当使用 class-based-views (CBVs) 和 Django Rest Framework (DRF) 时,继承自 ApiView class 并将 permission_classes 和 authentication_classes 设置为一个空元组。在下面找到一个例子。

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here

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

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

您需要添加此内容以防止默认会话身份验证:(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 ...():

删除 CSRF 检查并不总是唯一(或最佳)解决方案。实际上,它是 SessionAuthentication.

的重要安全机制

我在尝试使用 JWT 进行身份验证并执行 POST 请求时遇到了同样的问题。

我的初始设置如下所示:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.SessionAuthentication",
        "django_cognito_jwt.JSONWebTokenAuthentication",
    ),
    ...
}

由于 SessionAuthentication 在列表中首先被选中,因此引发了 CSRF 错误。我的解决方案很简单,只需更改顺序以始终首先检查 JWT 身份验证。像这样:

    "DEFAULT_AUTHENTICATION_CLASSES": (
        "django_cognito_jwt.JSONWebTokenAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ),

最后,对我来说,SessionAuthentication 仅在 django 管理面板中使用,99% 的请求转到使用 JWT 身份验证的 API。

您需要绝对确定要关闭 CSRF 保护。

  1. 创建文件 authentication.py 并将其放置在项目中的任意位置。例如,在文件夹 session_utils.
  2. 将此代码放入文件中:
from rest_framework.authentication import SessionAuthentication


class SessionCsrfExemptAuthentication(SessionAuthentication):
    def enforce_csrf(self, request):
        pass

  1. 当您想向您的视图发出 POSTPUTPATCHDELETE 请求时,请确保您已将 SessionAuthentication 更改为SessionCsrfExemptAuthentication 来自新文件。查看示例:
    @api_view(["POST"])
    @authentication_classes([SessionCsrfExemptAuthentication])
    @permission_classes([IsAuthenticated])
    def some_view(request) -> "Response":
        # some logic here
        return Response({})

此技巧允许您覆盖方法(通过)enforce_csrf 并且新的会话身份验证 class 将跳过 CSRF 检查。

✌️

对我来说,使用 django 3.1.5django rest framework 3.12 解决方案要容易得多。

我碰巧在 views.py 文件中定义了这两种方法:

@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def create_transaction(request):
    return Response(status=status.HTTP_200_OK)

def create_transaction(initial_data):
    pass

我的 urls.py:

urlpatterns = [
    path('transaction', views.create_transaction, name='transaction'),
]

Django 正在选择最新的并抛出错误。重命名两者之一解决了问题。

下面的代码将消除对 CSRF 的需求。即使是匿名用户也可以发送请求。

from typing import List, Any

class Object(APIView):
    authentication_classes: List = []
    permission_classes: List[Any] = [AllowAny]

    ...
    ...