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/json
和 Content-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' 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})
如果您正在为您的应用程序使用独占虚拟环境,您可以使用以下方法而不影响任何其他应用程序。
您观察到的情况发生是因为 rest_framework/authentication.py
在 SessionAuthentication
的 authenticate
方法中有此代码 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 保护。
- 创建文件
authentication.py
并将其放置在项目中的任意位置。例如,在文件夹 session_utils
.
- 将此代码放入文件中:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
pass
- 当您想向您的视图发出
POST
、PUT
、PATCH
或 DELETE
请求时,请确保您已将 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.5
和 django 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]
...
...
我知道有关于 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/json
和 Content-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' 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})
如果您正在为您的应用程序使用独占虚拟环境,您可以使用以下方法而不影响任何其他应用程序。
您观察到的情况发生是因为 rest_framework/authentication.py
在 SessionAuthentication
的 authenticate
方法中有此代码 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 保护。
- 创建文件
authentication.py
并将其放置在项目中的任意位置。例如,在文件夹session_utils
. - 将此代码放入文件中:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
pass
- 当您想向您的视图发出
POST
、PUT
、PATCH
或DELETE
请求时,请确保您已将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.5
和 django 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]
...
...