Django 安全之跨站点请求伪造(CSRF)保护
by:授客 QQ:1033553122
测试环境
Win7
Django 1.11
跨站点请求伪造(CSRF)保护
中间件配置
默认的CSRF中间件在MIDDLEWARE中定义并处于激活状态。如果需要变更默认配置,修改settings.py中的MIDDLEWARE配置即可,如下,假设要开启CSRF,确保列表包含 'django.middleware.csrf.CsrfViewMiddleware',并且其位置位于其它会对CSRF攻击进行处理的中间件之前,假设要禁用CSRF中间件,去掉列表中的'django.middleware.csrf.CsrfViewMiddleware',或者采用注释方式,把 'django.middleware.csrf.CsrfViewMiddleware' 注释掉。注意:更改配置后需要重启web服务器。
MIDDLEWARE = [
……,
'django.middleware.csrf.CsrfViewMiddleware',
……
]
如果CSRF中间件被禁用(不推荐),又想对特定视图启用中间件保护,则可以针对特定视图使用csrf_protect()修饰器,如下:
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def specific_view(request):
do something
参考链接:https://docs.djangoproject.com/en/2.1/ref/csrf/#django.views.decorators.csrf.csrf_protect
相反的,如果中间件已经开启,但是又不想针对特定视图使用中间件保护,则可以针对特定视图使用csrf_exempt() 修饰器
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def specific_view(request):
do something
参考链接:https://docs.djangoproject.com/en/2.1/ref/csrf/#utilities
html模板配置
开启CSRF中间件的情况下,要在html模板中为使用post方法的form表单新增 csrf_token tag,如下:
<form method="post">
{% csrf_token %}
<input ...>... </input>
...
</form>
注意:如果被渲染的view视图未使用csrf_token模板标签,Django可能不会设置CSRF token cookie。这种情况下,假如有必要,可以使用Django提供的 @ensure_csrf_cookie()装饰器强制view视图发送CSRF cookie。
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie()
def specific_view(request):
do something
view视图函数
对应的视图函数中,确保使用了RequestContext来渲染response,以便{%csrf_token %}可以正常运行。因为 render()函数使用了RequestContext,所以一般的view中,使用render()也是可以的。
前端js脚本
注意:如果已开启CSRF 的情况下,需要给请求添加X_CSRFTOKEN 请求头,否则会报403错误
/**
* 验证不需要CSRF保护的HTTP方法名(GET|HEAD|OPTIONS|TRACE
*/
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
/**
* 获取cookie项的值
* key:cookie名称
*/
function getCookie(key) {
var cookies = document.cookie.split(';'); // 获取每个cookie项(不含会话id)
var value = undefined;
for (var i in cookies) {
var kv = cookies[i].split('='); // 每个cookie项的名称和cookie的值
var temp_key = kv[0].replace(' ', ''); // 获取的cookie项有多个值,第二个开始,键 的值 的左侧会加个空格
if (key == temp_key) {
var value = kv[1];
break;
}
}
return value;
}
/**
* Created by shouke on 2018/11/1.
*/
//全局变量设置
var loginURL = '/platform/api/v1/login'; // 注册api地址
var loginFormID = 'login-form'; // 登录表单ID
var registerRightNowID = 'registerRightNow' // 立即注册链接ID
/**
* 初始化
*/
$(document).ready(function() {
// 设置顶部导航
setTopNav();
var resources = $.session.get('resources');
if (resources) {
resources = JSON.parse(resources).resources;
// 设置立即注册url
$("#" + registerRightNowID).attr("href", resources.register_url.url);
} else {
alert('获取资源失败,导致设置立即注册连接失败');
}
// 为登录表单绑定提交事件
$('#' + loginFormID).submit(function() {
/**
* 提交登录信息
*/
var dataArray = $('#' + loginFormID).serializeArray();
var data = {}
$.each(dataArray, function () {
data[this.name] = this.value;
});
var csrfToken = getCookie('csrftoken');
if (csrfToken == undefined) {
alert('获取Cookie失败');
return false;
}
$.ajax({
type: "POST",
url: loginURL,
async: false,
data: data,
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFTOKEN", csrfToken);
}
},
success: function (result) {
if (result.success == 'true') {
alert(result.msg);
$.session.remove('resources'); // 清空登录前获取的资源,以便重新获取资源
// 重定向返回登录前的页面
window.location.href = result.next;
} else {
alert(result.msg + "," + result.reason);
}
},
error: function(XmlHttpRequest, textStatus, errorThrown) {
alert('登录请求失败' + XmlHttpRequest.responseText);
}
});
return false;
});
});
注意:这里最后面添加的return false; 主要是用于 避免跳转到django后台的返回结果数据页
参考链接
https://docs.djangoproject.com/en/2.1/ref/csrf/#using-csrf
https://docs.djangoproject.com/en/2.1/topics/security/#cross-site-request-forgery-csrf-protection