什么是CSRF

维基百科:

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

攻击细节:

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

防御措施:

检查Referer字段

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。

这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

添加校验token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

Django中防范CSRF

Django的中间件

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',
]

其中'django.middleware.csrf.CsrfViewMiddleware',就是负责验证 csrf_token 的。

写一个简单的用户登陆

urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^login/$', views.login),
    url(r'^index/$', views.index),
]

views.py

from django.shortcuts import render, HttpResponse, redirect

def login(request):
    if request.method == 'POST':
        username = request.POST.get('user')
        password = request.POST.get('pwd')
        if username == 'lcg' and password == '123':
            return redirect('/index/')
    return render(request, 'login.html')

def index(request):
    return HttpResponse('登录成功!')

login.html

<form action="/login/" method="post">
    <input type="text" placeholder="username" name="user">
    <input type="password" placeholder="password" name="pwd">
    <input type="submit" value="登录">
</form>

启动项目。进入login界面

当我输入用户名密码,点击登陆的时候,会报错:

下面我在login.html中加上csrf_token

<form action="/login/" method="post">
    {% csrf_token %}
    <input type="text" placeholder="username" name="user">
    <input type="password" placeholder="password" name="pwd">
    <input type="submit" value="登录">
</form>

此时再登陆就可以成功登陆!

审查元素看一下此时的Elements与之前有何不同:

此时,其实是多了一个input框,type="hidden"属性是隐藏的功能。

我们知道,防范CSRF攻击的一个办法可以是请求的时候带一个 token,到服务器验证 token 的有效性。django 对于 csrf 的防御主要在 middleware 实现,项目的settings.py中包含 django.middleware.csrf.CsrfViewMiddleware 这个中间件,默认是开始 csrf 防御的。每次在模板里写 form 时都知道要加一个 {% csrf_token %} ,如果是Ajax请求,也要携带 这个值,否则验证不通过会报Forbidden的错。

Ajax登录:

urls.py

from django.conf.urls import url

from . import views
urlpatterns = [
    url(r'^login/$', views.login),
    url(r'^index/$', views.index),
]

views.py

from django.shortcuts import render, HttpResponse, redirect
import json

def login(request):
    if request.method == 'POST':
        username = request.POST.get('user')
        password = request.POST.get('pwd')
        loginResponse = {"username": None, "error_msg": None}
        if username == 'lcg' and password == '123':
            loginResponse["username"] = username
        else:
            loginResponse["error_msg"] = "username or password is wrong!"
        return HttpResponse(json.dumps(loginResponse))
    return render(request, 'login.html')

def index(request):
    return HttpResponse('登录成功!')

login.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>ajax登录</title>
</head>
<body>

{% csrf_token %}
<input type="text" placeholder="username" id="user">
<input type="password" placeholder="password" id="pwd">
<button id="btn">登录</button>
<span id="error"></span>

<script src="/static/js/jquery-3.2.1.min.js"></script>
<script>
    $("#btn").on('click', function () {
        $.ajax({
            url: "/login/",
            type: "POST",
            data: {
                user: $("#user").val(),
                pwd: $("#pwd").val(),
                csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
            },
            success: function (arg) {
                var arg = JSON.parse(arg);
                if (arg.username) {
                    location.href = "/index/"
                }
                else {
                    $("#error").text(arg.error_msg).css("color", "red")
                }
            }
        })
    });
</script>
</body>
</html>

效果:

输入错误:

输入正确:

Django Ajax登录 防止CSRF的更多相关文章

  1. python 全栈开发,Day87(ajax登录示例,CSRF跨站请求伪造,Django的中间件,自定义分页)

    一.ajax登录示例 新建项目login_ajax 修改urls.py,增加路径 from app01 import views urlpatterns = [ path('admin/', admi ...

  2. 关于Django Ajax CSRF 认证

    CSRF(Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的 ...

  3. Django 博客项目01 数据库设计与验证码校验+Ajax登录

    数据库设计 from django.db import models from django.contrib.auth.models import AbstractUser class UserInf ...

  4. python学习-- Django Ajax CSRF 认证

    使用 jQuery 的 ajax 或者 post 之前 加入这个 js 代码:http://www.ziqiangxuetang.com/media/django/csrf.js /*======== ...

  5. Django(十二)视图--利用jquery从后台发送ajax请求并处理、ajax登录案例

    一.Ajax基本概念 [参考]:https://www.runoob.com/jquery/jquery-ajax-intro.html 异步的javascript.在不全部加载某一个页面部的情况下, ...

  6. Django学习系列之CSRF

    Django CSRF 什么是CSRF CSRF, Cross Site Request Forgery, 跨站点伪造请求.举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果 某个用户已经登录到 ...

  7. 玩转Django的POST请求 CSRF

    玩转Django的POST请求 CSRF 不少麻油们玩django都会碰到这个问题,POST请求莫名其妙的返回 403 foribidden,希望这篇博文能解答所有问题 三种方法 To enable ...

  8. Django ajax MYSQL Highcharts<1>

    Another small project with django/Ajax/Mysql/Highcharts. 看下效果图  - delivery dashboard .嘿嘿 是不是还蛮好看的. 废 ...

  9. django ajax报错解决:You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set.

    Django版本号:1.11.15 django中ajax请求报错:You called this URL via POST, but the URL doesn't end in a slash a ...

随机推荐

  1. POJ 1753 bfs+位运算

    T_T ++运算符和+1不一样.(i+1)%4 忘带小括号了.bfs函数是bool 型,忘记返回false时的情况了.噢....debug快哭了...... DESCRIPTION:求最少的步骤.使得 ...

  2. Awk 从入门到放弃(3) —- 内置变量

    转:http://www.zsythink.net/archives/1374 NF :当前行的字段个数 NR:  行号 FNR: 各文件分别计数的行号 RS: 输入行分隔符 ORS:输出行分隔符 内 ...

  3. bug 问题

    1. 图片 img 标签,在IE浏览器下会有空白 - 解决办法:display:block; 2. IE6 下父级没有宽高,不会触发haslayout. 触发原因:子级浮动,父级没有宽高,overfl ...

  4. jquery组件和插件写法

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  5. 自定义div 拖动。键盘上下左右键移动,ctrl+Q控制是否可以移动,ctrl+回车,返回初始状态

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  6. bzoj1053&&51nod1060

    题解: 其实就是求1-n之中拥有最多约数的数 一个数x的质因数分解为p1^e1*p2^e2*...*pn^en,则正因数的个数为(e1+1)(e2+1)...(en+1) 那么发现,正因数的个数和p没 ...

  7. iOS调用第三方地图App进行导航方法

    前言 App内根据手机上装载的地图App将其显示在弹出的选择框,选择对应地图跳转进入地图导航.需要用到- (BOOL)canOpenURL:(NSURL *)url NS_AVAILABLE_IOS( ...

  8. 2018-北航-面向对象567次OO作业分析与小结

    设计策略及其变化 第五次作业-多线程电梯 在这次作业一开始的大部分时间,我一直想着怎样设计最为完美,完全使用BlockingQueue,导致交作业前发现设计并不能满足指导书的要求.最后仓皇之中加了一个 ...

  9. nginx对上传文件大小的限制

    前几天项目组的一个小伙子过来跟我说,他本地上传的文件没问题,但是在测试环境上测试的时候上传失败. 后来发现时nginx对上传文件的大小做了限制,默认是1M,那么按照需求我们更改了限值为20M vi c ...

  10. WiFi Pineapple的Karma攻击与原理探究

    WiFi钓鱼是一种被炒烂了的安全威胁,你几乎每天都能从电视.网络媒体中看到因误连了恶意WiFi导致大笔资金失窃的案例,并且很多媒体语不惊人死不休的报道风格总是能把当事人的受害过程删减修饰到灵异的程度. ...