文章目录
一、跨站请求伪造(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(); // Does this cookie string begin with the name we want? 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
# FBV的装饰和普通函数的装饰方法一样
# CBV的装饰有点特殊,需要通过 method_decorator 方法来装饰 dispatch 方法
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'))