仅在 DRF 中进行身份验证时才检查 CSRF?

Dav*_*nes 10 django csrf django-csrf csrf-protection django-rest-framework

TLDR;如果客户端具有经过身份验证的会话,则我的 POST(到 DRF 端点)似乎仅受 CSRF 保护。这是错误的,并留下了登录CSRF攻击的应用程序选项。我怎样才能解决这个问题?

我开始为 ReactJS 前端构建 django rest 框架 API,我们希望通过 API 处理一切,包括身份验证。我们正在使用 SessionAuthentication。

如果我有一个经过身份验证的会话,那么 CSRF 完全按预期工作(当经过身份验证时,客户端应该设置一个 CSRF cookie,这需要与 POST 数据中的 csrfmiddlewaretoken 配对)。

但是,如果未通过身份验证,则似乎没有 POST 需要接受 CSRF 检查。包括已经创建的(基本)登录APIView。这使得该站点容易受到登录 CSRF漏洞的攻击。

有谁知道如何在未经身份验证的会话上强制执行 CSRF 检查?和/或 DRF 似乎如何绕过 CSRF 检查以进行登录?

下面是我的粗略设置...

设置.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}
Run Code Online (Sandbox Code Playgroud)

视图.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)
Run Code Online (Sandbox Code Playgroud)

网址.py:

urlpatterns = [
    url(r'^login/$', views.Login.as_view(), name='login'),
]
Run Code Online (Sandbox Code Playgroud)

预期行为:

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)
Run Code Online (Sandbox Code Playgroud)

实际行为:

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.
Run Code Online (Sandbox Code Playgroud)

Gwy*_*idD 7

Django REST Framework 在使用 SessionAuthentication 并且用户未通过身份验证时禁用 CSRF 令牌要求。这是在设计上不会弄乱不需要 CSRF 身份验证的其他身份验证方法(因为它们不是基于 cookie),您应该自己确保 CSRF 在登录请求时得到验证,并且在SessionAuthentication文档的最后一段中提到. 建议要么使用非 API 登录流程,要么确保基于 API 的登录流程受到全面保护。

您可以在登录时检查DRFSessionAuthentication如何强制执行 CSRF 验证,并以此为基础。

  • 是的,谢谢,我已经通过在我的 LoginView 的 `dispatch` 方法上使用 `@method_decorator(csrf_protect)` 解决了。一切都在工作和受到保护。 (3认同)