Django解决跨站请求伪造CSRF ---- 第16章

1. 基本应用

Django中通过生成随机字符串的方式对客户发送提交数据的请求(POST)进行验证,基本原理:客户端向服务端先发送GET请求,服务端会把随机字符串写到请求的页面上,客户端再来提交数据的时候需要带上这个随机字符串,服务端才会处理请求。默认中间件的配置中SCRF是开启的状态:

settings.py:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',	# CSRF
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

views.py:

def csrf(request):
    if request.method == 'GET':
        return render(request, 'csrf.html')
    else:
        return HttpResponse('ok')

Django中的 {% csrf_token %} 就是服务端生成的随机字符串,如果在页面上没有加上它,那么请求的时候就不会带上这个随机字符串:

csrf.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <input type="text">
    <input type="submit">
</form>
</body>
</html>

在提交数据的时候会 403 Forbidden的错误:
在这里插入图片描述
如果在页面上加上 {% csrf_token %},客户端在发送GET请求后,服务端返回的页面上就会显示这个随机字符串,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <input type="text">
    <input type="hidden" name="csrfmiddlewaretoken" value="a7brzXos3mDRZlmdWyJcKs72MJ4WtvXaZfa8xWUJq7E3dvNhFrWk3gesKdup3TR2">
    <input type="submit">
</form>
</body>
</html>

客户端直接带着这个随机字符串提交提交数据即可,不会再包请求拒绝的问题。

{{ csrf_token }}会在页面上直接显示这个随机字符串,而{% csrf_token %}会生成input标签,这是有区别的。

2. 全站禁用

如果全站都不想使用CSRF的验证,可以全局禁用处理里CSRF,只需要在配置文件中的中间件部分注释掉CSRF的过滤,

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',	
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

如果是这样,客户端提交数据请求时不会进行CSRF的处理。

3. 局部禁用

如果不想在某些页面提交请求数据时做CSRF验证,这个时候 django.middleware.csrf.CsrfViewMiddleware 中间件是(全局)开启的状态,那就可以通过 装饰器 的方式单独禁用这个页面发送提交数据请求的CSRF的验证,只需要 在视图函数中加入csrf_exempt装饰器

views.py:

from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def csrf(request):
    if request.method == 'GET':
        return render(request, 'csrf.html')
    else:
        return HttpResponse('ok')
4. 局部使用

如果在全局禁用的状态下,局部使用CSRF验证。需要在视图函数中加上 `` 装饰器,表示在这个视图函数对应的模板中提交数据的请求必须进行CSRF验证:

views.py:

from django.views.decorators.csrf import csrf_protect


@csrf_protect
def csrf(request):
    if request.method == 'GET':
        return render(request, 'csrf.html')
    else:
        return HttpResponse('ok')

如果在模板中没有加上 {% csrf_token%} 同样是请求被拒绝的:
在这里插入图片描述

加入csrf认证后服务端只接收来自本站提交数据的请求,别的站点发送的是不被接收的。所以,csrf验证是可以防止别的站点发送提交数据的请求到本站。但是,csrf认证不是完全可以避免的,如过是其它站点获取到了本站的csrf,带着它去请求本站,服务端也会认为是自己的站点发送的请求,会选择接收请求的。所以,csrf认证只是在一定程度上保护了站点。

5. AJAX携带CSRF提交数据

如果AJAX不携带CSRF字符串提交数据,也会出现 403 Forbidde 报错,所以 AJAX提交数据也是要携带CSRF字符串的。AJAX携带CSRF提交数据的方法有两种,一种是把页面上的csrf随机字符串放在 data 中传到后端,另一种是放在 把csrf随机字符串放在请求头中 。首先探讨第一种方法:

views.py:

from django.views.decorators.csrf import csrf_protect

@csrf_protect
def csrf(request):
    if request.method == 'GET':
        return render(request, 'csrf.html')
    else:
        return HttpResponse('ok')

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form>
    <input type="text" id="user">
    {% csrf_token %}
    <input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script>
    $(function () {
        submitForm();
    });

    function submitForm() {
        $('#submit').click(function () {
            var user = $('#user').val();
            var csrfmiddlewaretoken = $('input[name="csrfmiddlewaretoken"]').val()
            $.ajax({
                url: '/index.html/', //可以不写,默认发送到本页面对应的视图函数中
                type: 'post',
                data: {
                    'user': user,
                    'csrfmiddlewaretoken': csrfmiddlewaretoken  //csrfmiddlewaretoken也可以不获取直接拿"{{csrf_token}}"
                },
                success: function (arg) {
                    alert(arg) //ok
                }
            })
        })
    }
</script>
</html>

在这里插入图片描述
在这里插入图片描述
第二种方法,把cookie放在请求头中。首先引入插件 jquery.cookie.js,这个插件专门对cookie进行操作的,通过cokkie名可以直接获取cookie的值,也还可以设置 cookie:
在这里插入图片描述
Django规定从 浏览器获取的cookie 传入到Django中要传到 请求头 中,要使用到headers:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form>
    <input type="text" id="user">
    <input type="button" value="提交" id="submit">
</form>
</body>
<script src="/static/js/jquery-3.5.1.min.js"></script>
<script src="/static/js/jquery.cookie.js"></script>
<script>
    $(function () {
        submitForm();
    });

    function submitForm() {
        $('#submit').click(function () {
            var user = $('#user').val();
            var token = $.cookie('csrftoken');
            $.ajax({
                url: '/csrf.html/', //可以不写,默认发送到本页面对应的视图函数中
                type: 'post',
                headers:{'X-CSRFToken':token},
                data: {
                    'user': user
                },
                success: function (arg) {
                    alert(arg) //ok
                }
            })
        })
    }
</script>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章