django 安全之防御 csrf
本文主要通过分析 django 源码 介绍 django 在 csrf (跨站请求伪造) 的防御措施, 默认大家对 csrf 有一定的了解
0x01
一般问到如何防御 csrf, 很多人都会给这么个答案,请求的时候带一个 token,到服务器验证 token 的有效性。原理是没错,但是如果让你设计这个防御系统,需要考虑哪些呢,我觉得有这么几点
- token 如何存储
- token 如何验证
- token 何时刷新
存储和验证相关联,有这么一种做法,每次用户登录生成随机值,存在数据库或者缓存中,请求的时候带上 token,每次去查询 token 有效性,这种方法其实就跟 session 验证用户是否登录一样。但问题是如果用户很多,验证的效率怎么保证。所以这种方法可行,但是成本不划算。第二种就是现在大多数的做法,cookie 里面带一个 csrf_token, 每次请求的时候也带上 token ,然后只要比较两者是否相同就行了,这样服务器没存任何东西,保证了效率。当然如果你的站点有 xss 之类的漏洞,cookie 也会泄露,同样不能保证这种方案的有效性。
token 刷新其实可以只要登录的时候刷新下,因为除非泄露,一般情况下 token 被暴力破解的概率不大。
0x02
下面看 django 如何实现的
django 对于 csrf 的防御主要在 middleware 实现,默认是开始 csrf 防御的,当然也可以关闭,单独对一些 view 开启,不过不建议这么做
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
csrf_token = None
这部分取得 cookie 里面的 csrf_token, 如果不存在则生成一个新的 csrf_token
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
这个是取得 post 请求的 request_csrf_token
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
对于 put delete 或者 ajax 请求来说,需要把 token 放到 headers 里面,名字为 X-CSRFToken
最后只要比较 csrf_token 和 request_csrf_token 就能有效的防御 csrf
这里面有个特殊的地方,对于 http 来说,有可能存在中间人攻击,所以没法信任 HTTP_REFERER 这个 header ,但如果是 https, 做了下 HTTP_REFERER 的检查,只要 referer 不在信任域,一律拒绝。
referer = force_text(
request.META.get('HTTP_REFERER'),
strings_only=True,
errors='replace'
)
if referer is None:
return self._reject(request, REASON_NO_REFERER)
# Here we generate a list of all acceptable HTTP referers,
# including the current host since that has been validated
# upstream.
good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
# Note that request.get_host() includes the port.
good_hosts.append(request.get_host())
good_referers = ['https://{0}/'.format(host) for host in good_hosts]
if not any(same_origin(referer, host) for host in good_referers):
reason = REASON_BAD_REFERER % referer
return self._reject(request, reason)
在登录的时候需要加上 rotate_token, 来刷新 token
def rotate_token(request):
"""
Changes the CSRF token in use for a request - should be done on login
for security purposes.
"""
request.META.update({
"CSRF_COOKIE_USED": True,
"CSRF_COOKIE": _get_new_csrf_key(),
})
0x03
总结下,django 框架本身对于安全性这块做的挺好的,对于工程师来说是福也是祸,能够更专注逻辑实现,但是有时候也因为忽视导致安全事故的发生。所以安全研发意识其实很重要,形成一些好的习惯,防患于未然。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 用 python 提高效率之自如爬虫
下一篇: VSCode C++ 环境配置
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论