目录
一、CSRF & XSS
(一)CSRF
1. 含义
2. 攻击原理
(1)浏览器特点
(2)攻击方式
(二)XSS
1. 含义
2. 攻击原理
(三)二者区别
二、Django Ajax CSRF 防御
(一)令牌同步模式(Synchronizer Token Pattern,简称STP)
1. 原理
2. 设置验证
1. 原理
2. 设置验证
(三)X-Csrf-Token验证(Cookie-to-header token)
1. 原理
2. 设置验证
三、设定 CSRF 验证
(一)settings.py
(二)base.html
1. 针对 ajax 请求,body 里添加{% csrf_token %}
2. 针对 form 表单,form 表单里添加{% csrf_token %}
(三)JS
(1)方法1
(2)方法2
2. 针对 X-Csrf-Token验证(Cookie-to-header token)
(1)方法1
3. 针对所有模式的综合验证
(1)写入位置
(2)引入顺序
四、关闭 CSRF 防御功能
1. 方法1:全局禁用
2. 方法2:局部禁用
五、Django CSRF 的控制台Error
1. 原因
2. 解决
(二)CSRF verification failed. Request aborted.
1. 原因
(1)原因1:csrf_token 过期
(2)原因2:请求头的 csrf_token 与session存储的token不匹配
(3)原因3:模板里没有csrfmiddlewaretoken
跨站请求伪造(Cross-site Request Forgery )。
同个浏览器上,无论是目标网站A还是其他网站B,发送对目标网站A的HTTP请求,都会自动带上目标网站A的cookie,发送给服务端。
同个浏览器上,当用户登录了目标网站(已登录状态),再进入攻击网站,攻击网站发送对目标网站的HTTP请求,则会自动带上“已登录的目标网站”的cookie,就可以进行攻击服务端。
跨站脚本攻击(Cross-Site Scripting,为了和 CSS 区分,则简称 XSS)。
在目标网站上注入恶意脚本进行攻击。(攻击者提交恶意代码,浏览器再执行恶意代码,篡改了网页)
CSRF 是 HTTP 问题,XSS 是代码注入问题。
CSRF需登录目标网站,XSS不用登录。
(1)找到数据库Session的session_csrftoken参数。成功登录网站后,后端随机产生一个csrftoken值,并把该值保存在数据库Session参数中。
(2)找到网页模板的隐藏csrfmiddlewaretoken名的标签。初始化某界面时,前端模板会根据{% csrf_token %}生成一个隐藏的name='csrfmiddlewaretoken'的input标签,存放后端随机生成的csrftoken值。
(3)用户进行 ajax post 请求。post请求时,csrfmiddlewaretoken值会一并放在请求数据(或头信息)中,发送给后端。后端会根据前端csrfmiddlewaretoken的 value值、和服务端数据库 public.django_session 表的 session_csrftoken参数值,判断两个csrf_token解密后的值是否一致。
(4)一致则正常访问,不一致则拒绝访问。
在第三点中。
(1)找到浏览器cookie的csrftoken。成功登录网站后,整个浏览器中Cookie的csrftoken属性,存放后端随机生成的csrftoken值。
(2)找到网页模板的隐藏csrfmiddlewaretoken名的标签。初始化某界面时,前端模板会根据{% csrf_token %}生成一个隐藏的 name='csrfmiddlewaretoken' 的input标签,存放后端随机生成的csrftoken值。
(3)用户进行 ajax post 请求。post请求时,csrfmiddlewaretoken值会一并放在请求数据(或头信息)中,发送给后端。后端会根据前端csrfmiddlewaretoken的 value值、和浏览器Cookie的csrftoken值,判断两个csrf_token解密后的值是否一致。
(4)一致则正常访问,不一致则拒绝访问。
在第三点中。
(1)确保同源政策。
(2)系统主要使用JavaScript进行交互。(因为恶意攻击无法读取cookie值并复制到HTTP请求头中)
(3)将cookie上的token复制到HTTP请求头中。
(4)后端验证HTTP请求头中token的存在和完整性。
(5)X-Csrf-Token头存在且完整,则正常访问,否则拒绝访问。
在第三点中。
以下主要针对基于 django 使用 ajax 发送 post 请求时的验证。
MIDDLEWARE里添加'django.middleware.csrf.CsrfViewMiddleware' # 设置CSRF检测功能
例如:
{% csrf_token %}
{% block content %} {% endblock %}
渲染后:
插入的子模板
因为若在 base.html 中加入 {% csrf_token %},是不在 form 表单的范围内。所以需要在 form 表单里添加。
例如:
后端将csrftoken传到前端(放置在隐藏标签里),发送post请求时,请求数据data中加上csrftoken,一起发送给后端。
在base.html中的script里写上。
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
后端将csrftoken传到前端(放置在隐藏标签里),发送post请求时,请求数据data中加上csrftoken。
在base.html中的script里写上。
$.ajaxSetup({
data: {csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()},
});
cookie中存在csrftoken,请求头headers中加上csrftoken。
在base.html中的script里写上。
前提:
① 引入jquery-3.1.1.min.js和jquery.cookie.js
② 引入顺序:jQuery库文件必须先于cookie文件。
$.ajaxSetup({
headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
});
前面两个验证的所有方法只能写在html中,不能写到外部js文件。因为{{ csrf_token }} 渲染在模板上,若以外部文件引入,则不能执行。
以下代码可以写在外部js文件里,并在 base.html 里引入该js文件。
该 js 文件的引入顺序必须在 jquery.js 文件之后。
/**
* 【 Cross Site Request Forgery TOKEN 】
* Forbidden 403 - CSRF check with an AJAX POST request in Django.
*/
jQuery(document).ajaxSend(function (event, xhr, settings) {
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// 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;
}
function sameOrigin(url) {
// url could be relative or scheme relative or absolute
let host = document.location.host; // host + port
let protocol = document.location.protocol;
let sr_origin = '//' + host;
let origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url === origin || url.slice(0, origin.length + 1) === origin + '/') ||
(url === sr_origin || url.slice(0, sr_origin.length + 1) === sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function safeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
});
不推荐关闭 CSRF 防御功能,因为取消了 CSRF 防御保护。
settings.py中,注释掉'django.middleware.csrf.CsrfViewMiddleware'
views.py中,导入from django.views.decorators.csrf import csrf_exempt;
并给要处理post数据的函数补充@csrf_exempt(post函数有request参数)。
django 中使用 jquery ajax post 数据出现 403 错误。
即,settings设置了Django CSRF检测功能(防止跨站请求伪造),需要在django发送post请求时进行字符串验证,但还没有设置CSRF cookie。
(1)第一种:关闭 CSRF 检测功能
(2)第二种:设定 CSRF 验证
(这两种方式在前面第三点中已介绍)
解决:重新登录(若还未写前端登录,则admin登录)
解决:清除浏览器缓存; 重新登录
解决:模板中(或表单内)添加 {% csrf_token %}