一、跨站请求伪造(CSRF)的概念
CSRF全称为Cross-site request forgery,中文意为跨站请求伪造。它是指一种网络攻击技术:不法分子盗用身份信息并伪造请求,进行并非你本意的操作。
-
csrf的案例: 假设我们在浏览器中逛支付王(就当做是个很垃圾的支付平台,转账连个转账密码确认都没有的那种,但确实是正规平台),且已经登陆。注意!这意味着浏览器保存了用户登录相关的cookie信息。 此时,右下角来了个弹窗,提示可以领取优惠券。但其实就是个病毒弹出的钓鱼网站。而我们又天真地打开了网站。结果页面出现一个按钮:点我领取优惠券。我们不假思索的点了下去,结果账户中的1000块钱被转走。 -
钱被转走的原因: 关键在于那个按钮上,它实际上发送的是一个转账给不法分子的http请求(伪造的请求)。由于我们之前已经正常登陆,所以该请求就会携带cookie信息,使钓鱼网站跳过了登录支付王的步骤,再加上支付王没做转账时的密码确认,最终被不法分子转走了我们的钱。
二、django的CSRF令牌
**CSRF通常是针对POST方法的!**django自带了一种预防CSRF攻击的方法,十分简单好用。
-
使用方法: **必须启用'django.middleware.csrf.CsrfViewMiddleware' 中间件!**该中间件默认是启用的。 在表单中: form标签内部任意位置写入{% csrf_token %} 即可: <form>
……
{% csrf_token %}
……
</form>
-
预防原理:
同源策略:现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。保证了一个WEB应用的资源(cookie等)只能被该应用本身访问。
该模板语法实际上就是生成了一个隐藏的name 属性为'csrfmiddlewaretoken' 的input输入框,这个input的值就是Django提供给表单的csrf_token。 csrf_token实质上是一串随机字符串,同表单一起发给客户端,用户提交请求时,附带这个字符串,服务器端进行验证,是否与先前发送的一致,一致则通过请求。 而由于同源策略,恶意网站是得不到这个临时数据的。虽然能通过cookie跳过登录身份验证,却无法跳过csrf_token验证。
三、在ajax中使用csrf_token(jQuery为例)
由于js代码中无法携带隐藏的input框,所以ajax请求中就要使用另外的方法来携带csrf_token。
-
第一种: 在页面中写入{% csrf_token %} ,然后在data对象中,获取模板语法生成的随机字符串,提交给后端(注意键名不要写错): <form>
{% csrf_token %}
</form>
<script>
$.ajax({
url:'',
type:'POST',
data:{"name":"hugh","csrfmiddlewaretoken":$('[name=csrfmiddlewaretoken]').val()},
……
})
</script>
-
第二种: 将以下代码复制后应用到我们的前端页面,它是django官方提供的获取csrf_token的代码: function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
如果你安装了js-cookie 前端库,那么上面的代码可以简化为: const csrftoken = Cookies.get('csrftoken');
然后,在页面中写入{% csrf_token %} ,在data中将上面代码获取到的scrftoken 常量写入即可: data: { "name": "hugh", "csrfmiddlewaretoken":csrftoken }
四、关于CSRF的装饰器
1. 单独指定需要csrf验证的视图
在settings.py文件中注释掉'django.middleware.csrf.CsrfViewMiddleware' 中间件,就关闭了全局的csrf验证。此时,如果想要让某些视图具有csrf验证功能,可以使用csrf_protect 装饰器进行装饰:
from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_view(request):
return render(request, "template.html")
之后,通过该视图的请求就会被进行csrf验证。
2. 单独指定忽略csrf验证的视图
如果开启了全局csrf验证,而某些视图不需要csrf验证,则可以使用csrf_exempt 装饰器装饰这些视图:
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator
class IndexView(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, 'index.html')
def post(self, request):
return redirect(reverse('index'))
|