Clo*_*oud 3 django csrf django-rest-framework
我正在尝试对向匿名用户开放的 Django Rest API 强制执行 CSRF。为此,我尝试了两种不同的方法:
CSRFAPIView从一个基本视图扩展选定的 API 视图,该视图@ensure_csrf_cookie在调度方法上有一个注释。enforce_csrf()无论用户是否登录都适用。在这两种方法中,CSRF 检查似乎都在表面上起作用。如果 cookie 中缺少 CSRF 令牌或令牌长度不正确,端点将返回403 - Forbidden. 但是,如果我编辑 cookie 中的 CSRF 令牌的值,则该请求将被毫无问题地接受。所以我可以使用 CSRF 的随机值,只要它的长度正确。
此行为似乎偏离了常规 Django 登录视图,其中 CSRF 的内容确实很重要。我正在本地设置中使用 debug/test_environment 标志进行测试。
我在 DRF 中进行的自定义 CSRF 检查未经过深入验证的原因可能是什么?
自定义身份验证的代码片段:
class RestCsrfAuthentication(SessionAuthentication):
def authenticate(self, request):
self.enforce_csrf(request)
rotate_token(request)
return None
Run Code Online (Sandbox Code Playgroud)
并在设置中:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'csrfexample.authentication.RestCsrfAuthentication',
]
}
Run Code Online (Sandbox Code Playgroud)
实际上,Django 中 CSRF 令牌的具体内容并不重要。
\nDjango 安全团队成员对与您类似的问题的回复如下:
\n\n\n我们的 CSRF 代币的工作方式非常简单。每个表单都包含一个 CSRF 令牌,它与 CSRF cookie 相匹配。在处理受保护的表单之前,我们确保提交的令牌与 cookie 匹配。这是服务器端检查,但它不会针对存储的服务器端值进行验证。由于远程攻击者不应该能够在您的域上读取或设置任意 cookie,因此这可以保护您。
\n由于我们只是将 cookie 与发布的令牌进行匹配,因此数据并不敏感(事实上它完全是任意的 - “zzzz”的 cookie 工作得很好),因此轮换/过期建议不适用没有任何区别。如果攻击者可以在您的域上读取或设置任意 cookie,则所有形式的基于 cookie 的 CSRF 保护都会被破坏。
\n
(实际上,由于长度要求,“zzzz”不起作用,但稍后会详细介绍。)我建议阅读整个邮件列表消息以获得更全面的理解。其中有关于 Django 在框架中的独特之处的解释,因为 CSRF 保护独立于会话。
\n我通过Django 文档上的常见问题解答项目找到了该邮件列表消息:
\n\n\n发布任意 CSRF 令牌对(cookie 和 POST 数据)是否存在漏洞?
\n不,这是设计使然。如果没有中间人攻击,攻击者就无法向受害者\xe2\x80\x99s浏览器发送CSRF令牌cookie,因此成功的攻击需要获取受害者\xe2\x80\通过 XSS 或类似方式获取 x99s browser\xe2\x80\x99s cookie,在这种情况下,攻击者通常不需要 \xe2\x80\x99s 的 CSRF 攻击。
\n一些安全审核工具将此标记为问题,但如前所述,攻击者无法窃取用户\xe2\x80\x99s浏览器\xe2\x80\x99s CSRF cookie。\xe2\x80\x9c窃取\xe2\x80\x9d或使用Firebug、Chrome开发工具等修改您自己的令牌,\xe2\x80\x99不是一个漏洞。
\n
(强调我的。)
\n该消息来自 2011 年,但它仍然有效,为了证明这一点,让我们看一下代码。Django REST FrameworkSessionAuthentication和ensure_csrf_cookie装饰器都使用 Django 核心CsrfViewMiddleware (源)。在该中间件类的process_view()方法中,您将看到它获取 CSRF cookie(csrftoken默认命名的 cookie),然后获取发布的 CSRF 令牌(POST 数据的一部分,并回退到读取标X-CSRFToken头)。之后,它运行_sanitize_token()在 POSTed/X-CSRFToken 值上运行。此清理步骤是检查令牌长度是否正确的地方;这就是为什么当您提供较短或较长的令牌时,您会按预期收到 403。
之后,该方法继续使用函数_compare_salted_tokens()比较两个值。如果您阅读该函数以及它进行的所有进一步调用,您将发现它归结为检查两个字符串是否匹配,基本上不考虑字符串的值。
\n\n此行为似乎偏离了常规 Django 登录视图,其中 CSRF 的内容确实很重要。
\n
不,即使在内置登录视图中也没关系。我针对大多数默认的 Django 项目运行了这个curl 命令(Windows cmd 格式):
\ncurl -v\n -H "Cookie: csrftoken=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"\n -H "X-CSRFToken: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"\n -F "username=admin" -F "password=1234" http://localhost:8000/admin/login/\nRun Code Online (Sandbox Code Playgroud)\nDjango 返回一个会话 cookie(当然还有一个 CSRF cookie)。
\n请注意您重写的方式SessionAuthentication.authenticate():您可能已经知道这一点,但根据 DRF 文档,该方法应该返回一个(User, auth)元组,而不是None请求是否有会话数据,即如果请求来自登录用户。另外,我认为rotate_token()这是不必要的,因为此代码仅检查身份验证状态,而不关心实际对用户进行身份验证。(Django源代码说rotate_token()\xe2\x80\x9c 应该在登录\xe2\x80\x9d 上完成。)
| 归档时间: |
|
| 查看次数: |
2019 次 |
| 最近记录: |