什么是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. PHP:第四章——PHP数组转换,统计,相关函数

    <pre> <?php //数组转换,统计,相关函数 header("Content-Type:text/html;charset=utf-8"); //coun ...

  2. vector中erase用法注意事项

    以前就发现了vector中的erase方法有些诡异(^_^),稍不注意,就会出错.今天又一次遇到了,就索性总结一下,尤其是在循环体中用erase时,由于vector.begin() 和vector.e ...

  3. 静态函数和全局函数都没有this指针

    静态函数和全局函数都没有this指针

  4. 使用Mockito时遇到的一些问题

    最近在使用Mockito时遇到了几个比较tricking的问题,在这里记录一下. 1.如果方法的参数或者返回类型是泛型通配符相关的(如<?>,<? extends XXX>), ...

  5. DevExpress v18.1新版亮点——WinForms篇(六)

    用户界面套包DevExpress v18.1日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress WinForms v18.1 的新功能,快来下载试用新版本! ...

  6. sping整合redis,以及做mybatis的第三方缓存

    一.spring整合redis Redis作为一个时下非常流行的NOSQL语言,不学一下有点过意不去. 背景:学习Redis用到的框架是maven+spring+mybatis(框架如何搭建这边就不叙 ...

  7. beta阶段贡献分配实施

    作业要求[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2281] 要求1 每位组员的贡献分值   刘莹莹 王玉潘 潘世维 周昊 赵美增 ...

  8. rem 移动端适配

    1.从网易与淘宝的font-size思考前端设计稿与工作流 http://www.cnblogs.com/lyzg/p/4877277.html 2.淘宝弹性布局方案lib-flexible实践 ht ...

  9. BigPipe 大的页面分割成一个一个管道

    bigpipe创新驱动力  node实现 具体实现 过去十年,现代web站点变得更加动态和内容化,交互性也逐步增强, 传统的页面处理的方式却没有保持一样的速度发展,越来越不能满足用户对极致性能的追求. ...

  10. DS博客作业04--树大作业说明

    大作业题目说明 1.目录树 按照如下目录路径,设计一颗目录树保存.并能实现对目录树遍历.目录路径存在file.txt,格式如下: b.txt c\ ab\cd.txt a\bc.txt ab\d.tx ...