为什么Django/Django REST框架不深入验证CSRF令牌,即使使用强制CSRF?
我试图为Django Rest API强制执行CSRF,该API对匿名用户开放。 为此,我尝试了两种不同的方法:为什么Django/Django REST框架不深入验证CSRF令牌,即使使用强制CSRF?,django,django-rest-framework,csrf,Django,Django Rest Framework,Csrf,我试图为Django Rest API强制执行CSRF,该API对匿名用户开放。 为此,我尝试了两种不同的方法: 从一个CSRFAPIViewbase视图扩展选定的API视图,该视图在分派方法上具有@sure\u csrf\u cookie注释 使用基于SessionAuthentication的自定义身份验证类,该类应用于enforce\u csrf(),无论用户是否登录 在这两种方法中,CSRF检查似乎表面上起作用。如果cookie中缺少CSRF令牌或令牌的长度不正确,端点将返回一个403-
CSRFAPIView
base视图扩展选定的API视图,该视图在分派方法上具有@sure\u csrf\u cookie
注释enforce\u csrf()
,无论用户是否登录403-禁止的
。
但是,如果我在cookie中编辑CSRF令牌的值,则请求将被接受而不会出现问题。所以我可以使用CSRF的随机值,只要长度正确
这种行为似乎偏离了常规的Django登录视图,在该视图中,CSRF的内容确实很重要。我正在本地设置中测试,调试/测试_环境标志处于打开状态
我在DRF中的自定义CSRF检查未得到深入验证的原因可能是什么
自定义身份验证的代码片段:
类RestCsrfAuthentication(会话身份验证):
def身份验证(自我、请求):
自我强制执行csrf(请求)
轮换令牌(请求)
一无所获
在设置中:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'csrfexample.authentication.RestCsrfAuthentication',
]
}
实际上,Django中CSRF令牌的具体内容并不重要 对一位女士说: 我们的CSRF代币的工作方式非常简单。每个表单都包含一个与CSRF cookie匹配的CSRF令牌。在处理受保护的表单之前,我们确保提交的令牌与cookie匹配。这是一个服务器端检查,但不是根据存储的服务器端值进行验证。由于远程攻击者不应能够读取或设置域上的任意cookie,因此可以保护您 因为我们只是将cookie与发布的令牌进行匹配,所以数据并不敏感(事实上,它完全是任意的——一个“zzzz”cookie工作得很好),所以轮换/过期建议没有任何区别。如果攻击者可以在您的域上读取或设置任意cookie,则所有形式的基于cookie的CSRF保护都将被破坏,完全停止 (实际上“zzzz”不起作用,因为长度要求,但稍后会有更多内容。)我建议阅读完整的邮件列表信息,以便更全面地理解。有人解释了Django在框架中的特殊性,因为CSRF保护独立于会话 我通过以下途径找到该邮件列表消息: 发布任意CSRF令牌对(cookie和POST数据)是否存在漏洞 不,这是设计的。如果没有中间人攻击,攻击者就无法向受害者的浏览器发送CSRF令牌cookie,因此成功的攻击需要通过XSS或类似方式获得受害者的浏览器cookie,在这种情况下,攻击者通常不需要CSRF攻击 一些安全审计工具将此标记为问题,但如前所述,攻击者无法窃取用户浏览器的CSRF cookie使用Firebug、Chrome开发工具等“窃取”或修改您自己的令牌不是一个漏洞。 (我的重点。) 这条消息来自2011年,但它仍然有效,为了证明这一点,让我们看看代码。Django REST框架的
SessionAuthentication
和sure\u csrf\u cookie
decorator都使用核心Django的CsrfViewMiddleware
。在该中间件类中,您将看到它获取CSRF cookie(默认情况下名为csrftoken
的cookie),然后获取已发布的CSRF令牌(已发布数据的一部分,回退到读取X-csrftoken
头)。之后,它将在POSTed/X-CSRFToken值上运行。此清理步骤用于检查正确的令牌长度;这就是为什么当您提供更短或更长的代币时,您会得到预期的403
之后,该方法继续使用比较两个值。如果您阅读该函数,以及它进行的所有进一步调用,您将看到它归结为检查两个字符串是否匹配,基本上不考虑字符串的值
这种行为似乎偏离了常规的Django登录视图,在该视图中,CSRF的内容确实很重要
不,即使在内置的登录视图中也不重要。我对一个默认的Django项目运行了这个curl命令(Windows cmd格式):
curl-v
-H“Cookie:csrftoken=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvxyzabcdefghijkl”
-H“X-CSRFToken:ABCDEFGHIJKLMNOPQRSTUVWXYZABCDFGHIJKLMNOPQRSTUVWXYZABCDFGHIJKL”
-F“用户名=管理员”-F“密码=1234”http://localhost:8000/admin/login/
Django返回了一个会话cookie(当然还有一个CSRF cookie)
请注意重写会话身份验证。authenticate():您可能已经知道这一点,但是如果请求包含会话数据,即请求来自登录用户,则该方法应该返回
(User,auth)
元组,而不是None
。另外,我认为rotate\u token()
是不必要的,因为这段代码只检查身份验证状态,而与实际验证用户无关。(Django说,rotate_token()
“应该在登录时完成”。只是一个快速检查:您是否尝试禁用debug/test_env标志?我也在非调试环境中尝试过。我已经将它与调试模式下的其他CSRF行为(登录页面)进行了比较,并且在登录页面上的行为更为严格。您能分享这两种方法的实现细节吗?(使用#2,我假定您正在覆盖会话身份验证.authenticate()
?)