确保您构建的站点安全,对于专业的Web应用程序开发人员至关重要。
Django框架现在已经非常成熟,大多数常见安全问题都是由框架本身以某种方式解决的,但是没有任何安全措施是100%保证的,并且有新的威胁一直出现,所以这取决于您作为一个Web 开发人员确保您的网站和应用程序是安全的。
单一书籍章节中的Web安全性太深了。 本章将概述Django的安全功能,并提供有关保护Django网站的建议,以保护您的网站99%的时间,但是您应该随时了解网络安全的变化。
有关Web安全的更多详细信息,Django的安全问题档案以及维基百科的Web应用程序安全页面是一个很好的起点。
Django的内置安全特性
跨站点脚本(XSS)保护
XSS攻击允许用户将客户端脚本注入其他用户的浏览器。
这通常通过将恶意脚本存储在数据库中并将其显示给其他用户或通过让用户单击链接来实现,该链接将导致攻击者的JavaScript由用户的浏览器执行。 但是,只要数据在纳入页面之前没有充分消毒,XSS攻击就可能源于任何不受信任的数据源,如Cookie或Web服务。
使用Django模板可以保护您免受大多数XSS攻击。 但是,了解它提供的保护和限制是非常重要的。
Django模板转义对HTML特别危险的特定字符。 虽然这可以保护用户免受大多数恶意输入的侵扰,但并非完全万无一失。 例如,它不会保护以下内容:
<style class={{ var }}>...</style>
如果var设置为'class1 onmouseover = javascript:func()',则这可能导致未经授权的JavaScript执行,这取决于浏览器如何渲染不完美的HTML。 (引用属性值可以解决这种情况。)
使用带有自定义模板标签的is_safe,安全模板标签,mark_safe和关闭autoescape时,特别小心也很重要。
另外,如果您使用模板系统输出HTML以外的内容,则可能会有完全分离的字符和需要转义的单词。
在数据库中存储HTML时,您应该非常小心,特别是在检索并显示HTML时。
跨站点请求伪造(CSRF)保护
CSRF攻击允许恶意用户使用其他用户的凭据执行操作,而无需用户知晓或同意。
Django内置了针对大多数类型的CSRF攻击的保护,只要您已经在适当的时候启用并使用它。但是,与任何缓解技术一样,也存在限制。
例如,可以在全局或特定视图中禁用CSRF模块。如果你知道自己在做什么,你只应该这样做。如果您的网站拥有您无法控制的子域名,则还有其他限制。
CSRF保护通过在每个POST请求中检查一个随机数来实现。这可以确保恶意用户无法简单地将表单POST重播到您的网站,并让另一个登录用户无意中提交该表单。恶意用户必须知道随机数,这是用户特定的(使用cookie)。
使用HTTPS进行部署时,CsrfViewMiddleware将检查HTTP引用标头是否设置为同一来源(包括子域和端口)上的URL。由于HTTPS提供了额外的安全性,因此必须通过转发不安全的连接请求并将HSTS用于支持的浏览器,确保连接使用HTTPS。
使用csrf_exempt装饰器标记视图时要非常小心,除非绝对必要。
Django的CSRF中间件和模板标签为跨站请求伪造提供了易用的保护。
针对CSRF攻击的第一道防线是确保GET请求(以及其他“安全”方法,如9.1.1安全方法,HTTP 1.1,RFC 2616所定义)无副作用。然后可以通过执行以下步骤来保护通过“不安全”方法的请求,例如POST,PUT和DELETE。
如何使用它
要在您的视图中利用CSRF保护,请按以下步骤操作:
-
CSRD中间件在MIDDLEWARE_CLASSES设置中被默认激活。 如果您覆盖该设置,请记住,'django.middleware.csrf.CsrfViewMiddleware'应该放在任何视图中间件之前,该中间件假定已经处理了CSRF攻击。如果禁用了它,这不是建议的,您可以使用csrf_protect() 您希望保护的特定视图(请参阅下文)。
-
在任何使用POST表单的模板中,如果表单用于内部URL,则使用<form>元素中的csrf_token标记,例如:
<form action="." method="post">{% csrf_token %}
这不应该用于定位外部URL的POST表单,因为这会导致CSRF令牌泄露,从而导致漏洞。
-
在相应的视图函数中,确保使用'django.template.context_processors.csrf'上下文处理器。 通常,这可以通过以下两种方式之一完成:
1. 使用RequestContext,它始终使用'django.template.context_processors.csrf'(无论在“模板”设置中配置了哪些模板上下文处理器)。 如果您正在使用通用视图或contrib应用程序,那么您已被覆盖,因为这些应用程序始终使用RequestContext。- 手动导入并使用处理器生成CSRF令牌并将其添加到模板上下文中。 例如:
from django.shortcuts import render_to_response from django.template.context_processors import csrf def my_view(request): c = {} c.update(csrf(request)) # ... view code here return render_to_response("a_template.html", c)
你可能想编写自己的render_to_response()包装器,为你处理这一步。
AJAX
虽然上述方法可用于AJAX POST请求,但也有一些不便之处:您必须记住将CSRF令牌作为POST数据传递给每个POST请求。 出于这个原因,还有一种替代方法:在每个XMLHttpRequest上,将自定义X-CSRFToken标头设置为CSRF标记的值。 这通常更容易,因为许多JavaScript框架提供了允许在每个请求上设置标题的钩子。
作为第一步,您必须自己获得CSRF令牌。 标记的推荐来源是csrftoken cookie,如果您已为上述视图启用CSRF保护,则会设置该cookie。
默认情况下,CSRF令牌cookie被命名为csrftoken,但您可以通过CSRF_COOKIE_NAME设置控制cookie名称。
获取令牌很简单:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var 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;
}
var csrftoken = getCookie('csrftoken');
The above code could be simplified by using the jQuery cookie plugin[56]
to replace `getCookie`:
var csrftoken = $.cookie('csrftoken');
CSRF标记也存在于DOM中,但仅限于在模板中明确包含使用csrf_token。 该cookie包含规范标记; CsrfViewMiddleware会更喜欢Cookie到DOM中的令牌。 无论如何,如果令牌存在于DOM中,您可以保证拥有cookie,因此您应该使用cookie!
如果您的视图不是渲染包含csrf_token模板标签的模板,则Django可能不会设置CSRF标记cookie。 在表单动态添加到页面的情况下这很常见。 为了解决这个问题,Django提供了一个强制设置cookie的视图装饰器:ensure_csrf_cookie()。
最后,您必须在您的AJAX请求中真正设置标题,同时保护jQuery 1.5.1及更高版本中使用settings.crossDomain将CSRF标记发送到其他域:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
其他模板引擎
在使用与Django内置引擎不同的模板引擎时,可以在确保模板上下文中可用之后手动设置表单中的令牌。
例如,在Jinja2模板语言中,表单可能包含以下内容:
<div style="display:none">
<input type="hidden" name="csrfmiddlewaretoken"
value="{{ csrf_token }}">
</div>
您可以使用类似于上述AJAX代码的JavaScript来获取CSRF令牌的值。
网友评论