// Fork me on GitHub

Django csrf校验

 引入:

通常,钓鱼网站本质是本质搭建一个跟正常网站一模一样的页面,用户在该页面上完成转账功能

转账的请求确实是朝着正常网站的服务端提交,唯一不同的在于收款账户人不同。

如果想模拟一个钓鱼网站,就可是给用户书写一个form表单 对方账户的input框没有name属性,然后你自己悄悄提前写好了一个具有默认的并且是隐藏的具有name属性的input框。

如果想解决这个问题,当转账请求发送给服务端后,服务端会给各台机器返回一个随机实时字符串。下一次,如果还有请求向服务端发时,服务端会校验字符串,若对不上的话服务端就拒绝访问。这就是csrf校验。

那么form表单如何进行csrf校验呢?

你只需要在你的form表单内写一个{% csrf_token %}就可以了

Ajax请求设置csrf_token的三种方式

 示例:

urls.py

urlpatterns = [
    url(r'^transfer/', views.transfer),
]

settings.py

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]

第三种方式的js文件(官方文档套用就行了)

 myfile.js

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');

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

myfile.js

views.py

复制代码
def transfer(request):
    if request.method =='POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s 给 %s 转账 %s元' %(username,target_user,money))
    return render(request,'transfer.html')
复制代码

前端页面

 transfer.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_user:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
<button id="d1">发送ajax请求</button>


{% load static %}
<script src="{% static 'myfile.js' %}"></script>
<script>
$('#d1').click(function () {
$.ajax({
url:'',
type:'post',
// 第一种方式 自己手动获取
{#data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#}
// 第二种方式 利用模板语法
{#data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#}
// 第三种 通用方式 引入外部js文件
data:{'username':'hank'},
success:function (data) {
alert(data)
}
})
})
</script>
</body>
</html>

transfer.html

csrf装饰器

csrf装饰器作用在FBV上

装饰器模块导入:

from django.views.decorators.csrf import csrf_exempt,csrf_protect

当我们网站整体都校验csrf的时候 我想让某几个视图函数不校验

@csrf_exempt #给哪个视图函数加上,就不给哪个视图校验csrf

当我们网站整体都不校验csrf的时候 我想让某几个视图函数校验

@csrf_protect  #给哪个视图函数加上,就给哪个视图校验csrf

注意:验证同时需要把'django.middleware.csrf.CsrfViewMiddleware'注销掉

csrf装饰器作用在CBV上

当我们网站整体都不校验csrf的时候 我想让某几个视图函数校验

复制代码
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect


# @method_decorator(csrf_protect,name='post')  #第二种指名道姓地给某给方法装
class MyHome(View):
    @method_decorator(csrf_protect)  #第三种 给类中所有的方法都装
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request,*args,**kwargs)

    def get(self,request):
        return HttpResponse('get')
    # @method_decorator(csrf_protect)   #第一种方式
    def post(self,request):
        return HttpResponse('post')

注意:验证同时需要把'django.middleware.csrf.CsrfViewMiddleware'注销掉
复制代码

当我们网站整体都校验csrf的时候 我想让某几个视图函数不校验

 总结:给CBV加装饰器 推荐使用模块method_decorator

csrf_exempt 只能给dispatch方法装

 补充内容:

详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:
one click attack/session riding,缩写为:CSRF/XSRF。
攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。
盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。
  所以解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。   那么django中csrf认证怎么玩的呢?     官方文档中说到,检验token时,只比较secret是否和cookie中的secret值一样,而不是比较整个token。     我又有疑问了,同一次登录,form表单中的token每次都会变,而cookie中的token不便,
    django把那个salt存储在哪里才能保证验证通过呢。直到看到源码。   
def _compare_salted_tokens(request_csrf_token, csrf_token): # Assume both arguments are sanitized -- that is, strings of # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS. return constant_time_compare( _unsalt_cipher_token(request_csrf_token), _unsalt_cipher_token(csrf_token), ) def _unsalt_cipher_token(token): """ Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt the second half to produce the original secret. """ salt = token[:CSRF_SECRET_LENGTH] token = token[CSRF_SECRET_LENGTH:] chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt)) secret = ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok return secret        token字符串的前32位是salt, 后面是加密后的token, 通过salt能解密出唯一的secret。     django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。     同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。 Cookies Hashing:每一个表单请求中都加入随机的Cookie,由于网站中存在XSS漏洞而被偷窃的危险。 HTTP refer:可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。 验证码:用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。 令牌Token:一次性令牌在完成他们的工作后将被销毁,比较安全。 ...等等吧,还有很多其他的。      $.ajax({ url: "/cookie_ajax/", type: "POST", headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrftoken,并设置到请求头中 data: {"username": "chao", "password": 123456}, success: function (data) { console.log(data); } })     或者用自己写一个getCookie方法: 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');     每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。 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); } } });     注意:       如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。       如果你的视图渲染的HTML文件中没有包含 {% csrf_token %},Django可能不会设置CSRFtoken的cookie。       这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。 django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def login(request): pass     更多细节详见:Djagno官方文档中关于CSRF的内容

 

csrf  跨站请求伪造

~~~
ajax({
    
    data:{csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val(),
            // csrfmiddlewaretoken:'{{ csrf_token }}',
        }
})
ajax({
    
    url:'/test/',
    type:'post',
    headers:{
        "X-CSRFToken": $.cookie('csrftoken'),
    },
})


~~~

jquery操作cookie,文件地址<http://plugins.jquery.com/cookie/>

<https://www.cnblogs.com/clschao/articles/10480029.html>

~~~

<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'jquery.cookie.js' %}"></script>

~~~

同源机制

~~~
浏览器的一个安全机制,非同源不允许直接互相访问,同源:协议,域名(ip地址),端口号三项相同才是同源

简单请求和复杂请求
简单请求:
(1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
    HEAD
    GET
    POST
(2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。

简单请求只发送一次请求


复杂请求(非简单请求) 发送两次请求


* 简单请求和非简单请求的区别?

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers


请求的网站响应内容:
def index(request):
    a = {'name':'chao'}
    ret = JsonResponse(a)
    ret["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000" #让http://127.0.0.1:8000这个网址的所有请求都能通过同源机制获得我给他响应的数据
    ret["Access-Control-Allow-Origin"] = "*"
    ret["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000,http://127.0.0.1:8001,"
    ret["Access-Control-Allow-Headers"] = "content-type" #让所有的请求数据格式都能通过同源机制,
    return ret



~~~
<https://www.cnblogs.com/clschao/articles/10480029.html>
posted @ 2020-10-14 10:37  繁星春水  阅读(479)  评论(0编辑  收藏  举报
1 //2 3
//4