TLDR;如果客户端具有经过身份验证的会话,则我的POST(对DRF端点)似乎仅受CSRF保护。这是错误的,并将应用程序选项留给login CSRF攻击。我该如何解决这个问题?
我开始为ReactJS前端构建一个django rest框架API,我们希望通过API处理所有内容,包括身份验证。我们正在使用SessionAuthentication。
如果我有经过身份验证的会话,那么CSRF完全按预期工作(当auth"客户端应该设置CSRF cookie时,这需要与POST数据中的csrfmiddlewaretoken配对)。
但是,当不身份验证时,没有任何POST似乎要接受CSRF检查。包括已创建的(基本)登录APIView。这会使网站容易受到login CSRF攻击。
有人知道如何在未经过身份验证的会话上强制执行CSRF检查吗?和/或DRF如何绕过CSRF检查进行登录?
以下是我的粗略设置......
settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
views.py:
class Login(APIView):
permission_classes = (permissions.AllowAny,)
@method_decorator(csrf_protect) # shouldn't be needed
def post(self, request, format=None):
user = authenticate(
request,
username=request.POST['username'],
password=request.POST['password']
)
# ... perform login logic ...
def get(self, request, format=None):
"""
Client must GET the login to obtain CSRF token
"""
# Force generation of CSRF token so that it's set in the client
get_token(request)
return Response(None)
urls.py:
urlpatterns = [
url(r'^login/$', views.Login.as_view(), name='login'),
]
预期行为:
login_url = reverse('login')
login_details = {
'username': self.user.email,
'password': self.password,
}
client = APIClient(enforce_csrf_checks=True)
# Try to just POST to a CSRF protected view with no CSRF
response = client.post(reverse('login'), login_details)
# response status should be 403 Missing or incorrect CSRF
# GET the login API first to obtain CSRF
client.get(reverse('login'))
login_details['csrfmiddlewaretoken'] = client.cookies.get('csrftoken').value
# Now POST to the login API with the CSRF cookie and CSRF token in the POST data
response = client.post(reverse('login'), login_details)
# response status should now be 200 (and a newly rotated CSRF token delivered)
实际行为:
client = APIClient(enforce_csrf_checks=True)
# Try to just to a CSRF protected view with no CSRF
response = client.post(reverse('login'), login_details)
# BROKEN: response status is 200, client is now logged in
# Post to the exact same view again, still with no CSRF
response = client.post(reverse('login'), login_details)
# response status is now 403
# BROKEN: This prooves that this view is protected against CSRF, but ONLY for authenticated sessions.
答案 0 :(得分:2)
Django REST Framework在使用SessionAuthentication并且未对用户进行身份验证时禁用CSRF令牌要求。这是设计上不会弄乱其他不需要CSRF身份验证的身份验证方法(因为它们不是基于cookie),您应该自己确保CSRF在登录请求中得到验证,并且在{的最后一段中提到它{3}}。建议使用非API登录过程或确保基于API的登录过程受到完全保护。
您可以在登录时查看SessionAuthentication
documentation如何执行CSRF验证,并以此为基础进行观察。
答案 1 :(得分:0)
您可以创建强制CSRF的APIView子类。
from rest_framework import views
class ForceCRSFAPIView(views.APIView):
@classmethod
def as_view(cls, **initkwargs):
# Force enables CSRF protection. This is needed for unauthenticated API endpoints
# because DjangoRestFramework relies on SessionAuthentication for CSRF validation
view = super().as_view(**initkwargs)
view.csrf_exempt = False
return view
然后您需要做的就是将您的登录视图更改为从此后退
class Login(ForceCRSFAPIView)
# ...