【问题标题】:Django Rest Framework remove csrfDjango Rest 框架删除 csrf
【发布时间】:2015-09-01 11:24:36
【问题描述】:

我知道有关于 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 令牌。您可以在整个视图上使用它。这不应该工作吗?
  • 不,我仍然得到详细信息:“CSRF 失败:CSRF 令牌丢失或不正确。”信息。我从答案中得出结论,我应该删除默认身份验证。
  • 我在使用令牌身份验证时遇到了非常相似的情况。对于同一条船上的其他人:stackoverflow.com/questions/34789301/…

标签: django django-rest-framework csrf django-csrf


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    为什么会出现这个错误?

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

    当您没有在视图/视图集中定义任何 authentication_classes 时,DRF 使用此身份验证类作为默认值。

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

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

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

    那该怎么办?

    现在要禁用 csrf 检查,您可以创建一个自定义身份验证类 CsrfExemptSessionAuthentication,它扩展自默认的 SessionAuthentication 类。在这个身份验证类中,我们将覆盖在实际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 错误。

    【讨论】:

    • 对不起,我可能错过了重点,但绕过/禁用 csrf 保护是否存在安全风险?
    • @Paolo OP 需要禁用特定 API 的 CSRF 身份验证。但是,是的,禁用 csrf 保护会带来安全风险。如果需要为特定用例禁用会话身份验证,则可以使用此解决方案。
    • @RahulGupta 如果您不想强制执行_csrf,那么最好的方法是什么?
    • 在 DRF 中全局禁用 csrf。在 DEFAULT_AUTHENTICATION_CLASSES 设置中将SessionAuthentication 替换为上述CsrfExemptSessionAuthentication。解决我的问题。但是不确定风险有多大。
    • 你也可以使用这个类来制作一个装饰器来更容易地重用它:def ignore_csrf(view_func): return authentication_classes([CsrfExemptSessionAuthentication])(view_func) 并且使用@ignore_csrf而不是@csrf_exempt
    【解决方案3】:

    我也遇到了同样的问题。我关注了这个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,
    )
    

    【讨论】:

    【解决方案4】:

    更简单的解决方案:

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

    【讨论】:

    • 仅供参考, authentication_classes 行似乎是关键。无论有没有 CsrfExemptMixin,对我来说都一样。
    【解决方案5】:

    如果你为你的应用程序使用了专属的虚拟环境,你可以使用下面的方法而不影响其他任何应用程序。

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

    self.enforce_csrf(request)
    

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

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

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

    您必须在 Request 类中进行一些相关更改

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        我的解决方案显示失败。只是装饰我的班级。

        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
        

        【讨论】:

        • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
        【解决方案8】:

        修改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')
        

        应该完成工作!

        【讨论】:

        • 这对我来说适用于 python3 和 django 1.11,而且看起来最简单!
        • 装饰器对我不起作用。是否还有其他条件才能发挥作用?
        【解决方案9】:

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

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

        【讨论】:

        • 这和上面的问题有什么关系?
        • 意思是这个问题在你切换DNS并且还没有完全传播的时候会出现。如果应用程序的路由与 Django 正常会话不同,这就是原因。只是通知我遇到的一个边缘案例。这似乎是一个有点规范的资源,所以我想我会添加一个额外的资源。
        • 我不知道为什么这个答案在解释后被否决了。在 stackoverflow 上发布“边缘案例”的人拯救了我
        【解决方案10】:

        我尝试了上面的几个答案,觉得创建一个单独的类有点过火了。

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

        当使用基于类的视图 (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
        

        【讨论】:

          【解决方案11】:

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

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

          【讨论】:

            【解决方案12】:

            你需要添加这个来防止默认会话认证:(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 ...():
            

            【讨论】:

              【解决方案13】:

              删除 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 for me 仅在 django 管理面板中使用,99% 的请求都发送到使用 JWT auth 的 API。

              【讨论】:

                【解决方案14】:

                您需要绝对确定要关闭 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,并且新的会话身份验证类将跳过 CSRF 检查。

                ✌️

                【讨论】:

                  【解决方案15】:

                  对我来说,使用 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 正在选择最新的并抛出错误。重命名两者之一解决了这个问题。

                  【讨论】:

                    猜你喜欢
                    • 2017-07-02
                    • 2016-06-23
                    • 2021-06-19
                    • 2018-08-20
                    • 2021-02-28
                    • 2019-06-07
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多