django 发送POST请求
django发送post请求,如果直接发送,会出现csrf cookie not set 报错以及403错误。网上各种解决方法也比较多。
最靠谱的方法是引入csrf_exempt装饰器,如下既能解决。
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def XXX(req):
return ……
为什么要使用csrf_exempt装饰器
默认情况下,csrf的中间件是通过该方式被全局开启的,对所有的POST生效。可以在看默认的配置
MIDDLEWARE_CLASSES = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pagination.middleware.PaginationMiddleware',
]
这里的django.middleware.csrf.CsrfViewMiddleware
定义了csrf的中间件。
定义这个中间件的目的是整理POST请求的模拟安全,我们需要在post请求体中带上token,防止post请求的仿冒。
那么我们可以采用这种csrf_exempt的方式来进行,但是这种方式是不安全的,因为会去掉了token机制,在服务端不进行token的验证,我们使用下面的方法。
在form中带上{% csrf_token %}
配置如下所示:
<form id="contact" action="{% url 'blog:sendmail' %}">
<fieldset>
{% csrf_token %}
<span class="error" id="name_error">Please enter name !</span>
<span class="error" id="email_error">Please enter email address !</span>
<span class="error" id="email_error2">Please enter valid email address !</span>
<span class="error" id="msg_error">Please enter message !</span>
<label for="name" id="name_label">Name (required)</label>
<input type="text" name="name" id="name" size="50" value="" class="text-input" />
<label for="email" id="email_label">Email (required)</label>
<input type="text" name="email" id="email" size="50" value="" class="text-input" />
<label for="subject" id="subject_label">Subject</label>
<input type="text" name="subject" id="subject" size="50" value="" class="text-input" />
<label for="msg" id="msg_label">Message</label>
<textarea rows="10" name="msg" id="msg" class="text-input"></textarea>
<br />
<input type="submit" name="submit" class="button" id="submit_btn" value="Send Message"/>
</fieldset>
</form>
在form中带上csrf_token
后会在其中生成一个token。
可以查看源文件看出,其中是这样解析的
<input type="hidden" name="csrfmiddlewaretoken" value="g8GAWhzYB3HGZPwHiVGEtoAkagVu6EtW">
我们可以看到这个带上了相应的token,放在隐藏的iuput标签中,所以如果是form表单直接使用的话,可以加上这个标签就可以了,如果使用ajax发送的话,需要在data里面加上相应数据。
var dataString = 'name='+ name + '&email=' + email + '&subject=' + subject + '&msg=' + msg + '&csrfmiddlewaretoken=' + csrfmiddlewaretoken +;
//alert (dataString);return false;
jQuery.ajax({
type: "POST",
url: "sendmail",
data: dataString,
success: function() {
jQuery('#contactform').html("<div id='message' class='ten columns'></div>");
jQuery('#message').html("<strong>Contact Form Submitted!</strong>")
.append("<p>We will be in touch soon.</p>")
.hide()
.fadeIn(1500, function() {
jQuery('#message');
});
}
});
此时去掉csrf_exempt装饰器
也可以请求通过了。
在生成表单的页面中生成csrf
在生成表单中的view中生成csrf:
c = csrf(request)
result_dict = {}
result_dict.update(c)
return render(request, 'csrf/index.html', result_dict)
对于POST请求,可以在目标view方法中对于post请求方法进行限制,否则可能出现服务器内部错误
在view中限制post请求:
from django.views.decorators.http import require_http_methods
@require_http_methods(["POST", ])
django的CsrfViewMiddleware原理
Django的实现并不是基于后端的,Django不会在服务端记录会话和对应的token值,而是在初次设置token的时候同时设置一个名为csrftoken的永久Cookie,并且Cookie的值与表单中csrfmiddlewaretoke的值相同。
提交之后,Django通过Cookie和提交的数据进行判断。
具有该Cookie之后,Django将不会每次请求都生成token,而是根据Cookie直接向前端返回token
在csrf中间件全局开启的情况下,对于使用POST方法打开的页面,并且提交的内容没有重要性、没有机密性的可以通过csrf_exempt来关闭
在views中导入csrf_exempt装饰器
from django.views.decorators.csrf import csrf_exempt
在函数前使用
@csrf_exempt
对于csrf中间件全局关闭的情况下,对少量页面开启csrf防护可以通过csrf_protect来开启
在views中导入csrf_protect装饰器
from django.views.decorators.csrf import csrf_protect
在函数前使用
@csrf_protect
可以自行检测csrf
通过检测referer实现
referrer = request.META.get('HTTP_REFERER')
通过将referrer与业务流程上正常的referer进行比较,处理非正常referer的请求
@require_http_methods(['POST', ])
@csrf_exempt
def refer_view(request):
referrer = request.META.get('HTTP_REFERER')
mail = request.POST.get('mail')
if referrer != "http://127.0.0.1:8000/test_csrf/" or mail is None or mail == '':
return HttpResponseRedirect("/test_csrf/")
return render(request, 'csrf/refer.html', {'result': mail})
Ajax中的csrf
AJAX中的CSRF与普通的使用差别不大,但是有两种实现方式
一种就是利用Django的CSRF实现的原理,利用前端脚本从Cookie中读取csrftoken的值并放在data中提交。
另一种就是在template中直接写入token,直接从template定义data中的tokenmiddlewaretoken字段。
//方式一:运行getCookie函数,从Cookie中读取token值
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');
input_data = "mail=" + $("#mail_input").val() + "&csrfmiddlewaretoken=" + csrftoken;
//方式二:在模版中写入csrf_token
input_data = "mail=" + $("#mail_input").val() + "&csrfmiddlewaretoken=" + "{{ csrf_token }}";
//ajax在使用的时候与平常相同
$.ajax({
url:'/test_csrf/ajax_api/',
data:input_data,
type:'post',
dateType:'text',
success:function(data){
$("#ajax_return").val(data);
},
error: function () {
alert("error");
}
})
在进行代码审查的时候,需要审查的settings.py中是否添加了'django.middleware.csrf.CsrfViewMiddleware',并且涉及POST请求的view中是否没有使用@csrf_exempt
另外,如果传输的数据非常重要,建议增加其他校验方式如手机验证码、图片验证码或其他第三方因素进行校验。