CSRF攻击

CSRF攻击概述

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。其他安全隐患,比如 SQL 脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御。然而,对于大多数人来说,CSRF 却依然是一个陌生的概念。即便是大名鼎鼎的 Gmail, 在 2007 年底也存在着 CSRF 漏洞,从而被黑客攻击而使 Gmail 的用户造成巨大的损失。

CSRF攻击原理

网站是通过cookie来实现登录功能的。而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去。那么这时候就存在一个漏洞了,如果你访问了一个别有用心或病毒网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如ICBC的转账请求)。那么因为在发送请求的时候,浏览器会自动的把cookie发送给对应的服务器,这时候相应的服务器(比如ICBC网站),就不知道这个请求是伪造的,就被欺骗过去了。从而达到在用户不知情的情况下,给某个服务器发送了一个请求(比如转账)。

防御CSRF攻击

CSRF攻击的要点就是在向服务器发送请求的时候,相应的cookie会自动的发送给对应的服务器。造成服务器不知道这个请求是用户发起的还是伪造的。这时候,我们可以在用户每次访问有表单的页面的时候,在网页源代码中加一个随机的字符串叫做csrf_token,在cookie中也加入一个相同值的csrf_token字符串。以后给服务器发送请求的时候,必须在body中以及cookie中都携带csrf_token,服务器只有检测到cookie中的csrf_tokenbody中的csrf_token都相同,才认为这个请求是正常的,否则就是伪造的。那么黑客就没办法伪造请求了。在Django中,如果想要防御CSRF攻击,应该做两步工作。第一个是在settings.MIDDLEWARE中添加CsrfMiddleware中间件。第二个是在模版代码中添加一个input标签,加载csrf_token。示例代码如下:

  • 服务器代码:

    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.gzip.GZipMiddleware',
    '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'
    ]
  • 模版代码:

    <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"/>

    或者是直接使用csrf_token标签,来自动生成一个带有csrf tokeninput标签:

    {% csrf_token %}

使用ajax处理csrf防御

如果用ajax来处理csrf防御,那么需要手动的在form中添加csrfmiddlewaretoken,或者是在请求头中添加X-CSRFToken。我们可以从返回的cookie中提取csrf 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 myajax = {
'get': function (args) {
args['method'] = 'get';
this.ajax(args);
},
'post': function (args) {
args['method'] = 'post';
this._ajaxSetup();
this.ajax(args);
},
'ajax': function (args) {
$.ajax(args);
},
'_ajaxSetup': function () {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
}
}; $(function () {
$("#submit").click(function (event) {
event.preventDefault();
var email = $("input[name='email']").val();
var money = $("input[name='money']").val(); myajax.post({
'url': '/transfer/',
'data':{
'email': email,
'money': money
},
'success': function (data) {
console.log(data);
},
'fail': function (error) {
console.log(error);
}
});
})
});

iframe相关知识

  1. iframe可以加载嵌入别的域名下的网页。也就是说可以发送跨域请求。比如我可以在我自己的网页中加载百度的网站,示例代码如下:

    <iframe src="http://www.baidu.com/">
    </ifrmae>
  2. 因为iframe加载的是别的域名下的网页。根据同源策略js只能操作属于本域名下的代码,因此js不能操作通过iframe加载来的DOM元素。

  3. 如果ifrmaesrc属性为空,那么就没有同源策略的限制,这时候我们就可以操作iframe下面的代码了。并且,如果src为空,那么我们可以在iframe中,给任何域名都可以发送请求。

  4. 直接在iframe中写html代码,浏览器是不会加载的。

XSS攻击

XSS(Cross Site Script)攻击又叫做跨站脚本攻击。他的原理是用户在使用具有XSS漏洞的网站的时候,向这个网站提交一些恶意的代码,当用户在访问这个网站的某个页面的时候,这个恶意的代码就会被执行,从而来破坏网页的结构,获取用户的隐私信息等。

XSS攻击场景

比如A网站有一个发布帖子的入口,如果用户在提交数据的时候,提交了一段js代码比如:<script>alert("hello world");</script>,然后A网站在渲染这个帖子的时候,直接把这个代码渲染了,那么这个代码就会执行,会在浏览器的窗口中弹出一个模态对话框来显示hello world!如果攻击者能成功的运行以上这么一段js代码,那他能做的事情就有很多很多了!

XSS攻击防御

  1. 如果不需要显示一些富文本,那么在渲染用户提交的数据的时候,直接进行转义就可以了。在Django的模板中默认就是转义的。也可以把数据在存储到数据库之前,就转义再存储进去,这样以后在渲染的时候,即使不转义也不会有安全问题,示例代码如下:

     from django.template.defaultfilters import escape
    from .models import Comment
    from django.http import HttpResponse
    def comment(request):
    content = request.POST.get("content")
    escaped_content = escape(content)
    Comment.objects.create(content=escaped_content)
    return HttpResponse('success')
  2. 如果对于用户提交上来的数据包含了一些富文本(比如:给字体换色,字体加粗等),那么这时候我们在渲染的时候也要以富文本的形式进行渲染,也即需要使用safe过滤器将其标记为安全的,这样才能显示出富文本样式。但是这样又会存在一个问题,如果用户提交上来的数据存在攻击的代码呢,那将其标记为安全的肯定是有问题的。示例代码如下:

     # views.py
    def index(request):
    message = "<span style='color:red;'>红色字体</span><script>alert('hello world');</script>";
    return render_template(request,'index.html',context={"message":message})
     # index.html

    那么这时候该怎么办呢?这时候我们可以指定某些标签我们是需要的(比如:span标签),而某些标签我们是不需要的(比如:script)那么我们在服务器处理数据的时候,就可以将这些需要的标签保留下来,把那些不需要的标签进行转义,或者干脆移除掉,这样就可以解决我们的问题了。这个方法是可行的,包括很多线上网站也是这样做的,在Python中,有一个库可以专门用来处理这个事情,那就是sanitizer。接下来讲下这个库的使用。

bleach库

bleach库是用来清理包含html格式字符串的库。他可以指定哪些标签需要保留,哪些标签是需要过滤掉的。也可以指定标签上哪些属性是可以保留,哪些属性是不需要的。想要使用这个库,可以通过以下命令进行安装:

pip install bleach

这个库最重要的一个方法是bleach.clean方法,bleach.clean示例代码如下:

import bleach
from bleach.sanitizer import ALLOWED_TAGS,ALLOWED_ATTRIBUTES @require_http_methods(['POST'])
def message(request):
# 从客户端中获取提交的数据
content = request.POST.get('content') # 在默认的允许标签中添加img标签
tags = ALLOWED_TAGS + ['img']
# 在默认的允许属性中添加src属性
attributes = {**ALLOWED_ATTRIBUTES,'img':['src']} # 对提交的数据进行过滤
cleaned_content=bleach.clean(content,tags=tags,attributes=attributes) # 保存到数据库中
Message.objects.create(content=cleaned_content) return redirect(reverse('index'))

相关介绍如下:

  1. tags:表示允许哪些标签。
  2. attributes:表示标签中允许哪些属性。
  3. ALLOWED_TAGS:这个变量是bleach默认定义的一些标签。如果不符合要求,可以对其进行增加或者删除。
  4. ALLOWED_ATTRIBUTES:这个变量是bleach默认定义的一些属性。如果不符合要求,可以对其进行增加或者删除。

bleach更多资料

  1. github地址: https://github.com/mozilla/bleach
  2. 文档地址: https://bleach.readthedocs.io/

clickjacking(点击劫持)攻击

clickjacking攻击又称作点击劫持攻击。是一种在网页中将恶意代码等隐藏在看似无害的内容(如按钮)之下,并诱使用户点击的手段。

clickjacking攻击场景

场景一

如用户收到一封包含一段视频的电子邮件,但其中的“播放”按钮并不会真正播放视频,而是链入一购物网站。这样当用户试图“播放视频”时,实际是被诱骗而进入了一个购物网站。

场景二

用户进入到一个网页中,里面包含了一个非常有诱惑力的按钮A,但是这个按钮上面浮了一个透明的iframe标签,这个iframe标签加载了另外一个网页,并且他将这个网页的某个按钮和原网页中的按钮A重合,所以你在点击按钮A的时候,实际上点的是通过iframe加载的另外一个网页的按钮。比如我现在有一个百度贴吧,想要让更多的用户来关注,那么我们可以准备以下一个页面:

<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<head>
<title>点击劫持</title>
<style>
iframe{
opacity:0.01;
position:absolute;
z-index:2;
width: 100%;
height: 100%;
}
button{
position:absolute;
top: 345px;
left: 630px;
z-index: 1;
width: 72px;
height: 26px;
}
</style>
</head>
<body>
这个合影里面怎么会有你?
<button>查看详情</button>
<iframe src="http://tieba.baidu.com/f?kw=%C3%C0%C5%AE"></iframe>
</body>
</html>

页面看起来比较简陋,但是实际上可能会比这些更精致一些。当这个页面通过某种手段被传播出去后,用户如果点击了“查看详情”,实际上点击到的是关注的按钮,这样就可以增加了一个粉丝。

clickjacking防御

像以上场景1,是没有办法避免的,受伤害的是用户。而像场景2,受伤害的是百度贴吧网站和用户。这种场景是可以避免的,只要设置百度贴吧不允许使用iframe被加载到其他网页中,就可以避免这种行为了。我们可以通过在响应头中设置X-Frame-Options来设置这种操作。X-Frame-Options可以设置以下三个值:

  1. DENY:不让任何网页使用iframe加载我这个页面。
  2. SAMEORIGIN:只允许在相同域名(也就是我自己的网站)下使用iframe加载我这个页面。
  3. ALLOW-FROM origin:允许任何网页通过iframe加载我这个网页。

Django中,使用中间件django.middleware.clickjacking.XFrameOptionsMiddleware可以帮我们堵上这个漏洞,这个中间件设置了X-Frame-OptionSAMEORIGIN,也就是只有在自己的网站下才可以使用iframe加载这个网页,这样就可以避免其他别有心机的网页去通过iframe去加载了。

SQL注入

所谓SQL注入,就是通过把SQL命令插入到表单中或页面请求的查询字符串中,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的。

场景

比如现在数据库中有一个front_user表,表结构如下:

class User(models.Model):
telephone = models.CharField(max_length=11)
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)

然后我们使用原生sql语句实现以下需求:

  1. 实现一个根据用户id获取用户详情的视图。示例代码如下:

         def index(request):
    user_id = request.GET.get('user_id')
    cursor = connection.cursor()
    cursor.execute("select id,username from front_user where id=%s" % user_id)
    rows = cursor.fetchall()
    for row in rows:
    print(row)
    return HttpResponse('success')

    这样表面上看起来没有问题。但是如果用户传的user_id是等于1 or 1=1,那么以上拼接后的sql语句为:

     select id,username from front_user where id=1 or 1=1
    
    

    以上sql语句的条件是id=1 or 1=1,只要id=1或者是1=1两个有一个成立,那么整个条件就成立。毫无疑问1=1是肯定成立的。因此执行完以上sql语句后,会将front_user表中所有的数据都提取出来。

  2. 实现一个根据用户的username提取用户的视图。示例代码如下:

     def index(request):
    username = request.GET.get('username')
    cursor = connection.cursor()
    cursor.execute("select id,username from front_user where username='%s'" % username)
    rows = cursor.fetchall()
    for row in rows:
    print(row)
    return HttpResponse('success')

    这样表面上看起来也没有问题。但是如果用户传的usernamezhiliao' or '1=1,那么以上拼接后的sql语句为:

     select id,username from front_user where username='zhiliao' or '1=1'
    
    

    以上sql语句的条件是username='zhiliao'或者是一个字符串,毫无疑问,字符串的判断是肯定成立的。因此会将front_user表中所有的数据都提取出来。

sql注入防御

以上便是sql注入的原理。他通过传递一些恶意的参数来破坏原有的sql语句以便达到自己的目的。当然sql注入远远没有这么简单,我们现在讲到的只是冰山一角。那么如何防御sql注入呢?归类起来主要有以下几点:

  1. 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。

  2. 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。比如:

     def index(request):
    user_id = "1 or 1=1"
    cursor = connection.cursor()
    cursor.execute("select id,username from front_user where id=%s",(user_id,))
    rows = cursor.fetchall()
    for row in rows:
    print(row)
    return HttpResponse('success')
  3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

  4. 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。

  5. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装。

在Django中如何防御sql注入

  1. 使用ORM来做数据的增删改查。因为ORM使用的是参数化的形式执行sql语句的。
  2. 如果万一要执行原生sql语句,那么建议不要拼接sql,而是使用参数化的形式。

Django学习笔记之安全的更多相关文章

  1. Django 学习笔记之四 QuerySet常用方法

    QuerySet是一个可遍历结构,它本质上是一个给定的模型的对象列表,是有序的. 1.建立模型: 2.数据文件(test.txt) 3.文件数据入库(默认的sqlite3) 入库之前执行 数据库同步命 ...

  2. Django 学习笔记之三 数据库输入数据

    假设建立了django_blog项目,建立blog的app ,在models.py里面增加了Blog类,同步数据库,并且建立了对应的表.具体的参照Django 学习笔记之二的相关命令. 那么这篇主要介 ...

  3. Django学习笔记(五)—— 表单

    疯狂的暑假学习之  Django学习笔记(五)-- 表单 參考:<The Django Book> 第7章 1. HttpRequest对象的信息 request.path         ...

  4. Django学习笔记(三)—— 型号 model

    疯狂暑期学习 Django学习笔记(三)-- 型号 model 參考:<The Django Book> 第5章 1.setting.py 配置 DATABASES = { 'defaul ...

  5. Django 学习笔记(二)

    Django 第一个 Hello World 项目 经过上一篇的安装,我们已经拥有了Django 框架 1.选择项目默认存放的地址 默认地址是C:\Users\Lee,也就是进入cmd控制台的地址,创 ...

  6. Django 学习笔记(五)模板标签

    关于Django模板标签官方网址https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.IF标签 Hello World/vi ...

  7. Django 学习笔记(四)模板变量

    关于Django模板变量官方网址:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.传入普通变量 在hello/Hell ...

  8. Django 学习笔记(三)模板导入

    本章内容是将一个html网页放进模板中,并运行服务器将其展现出来. 平台:windows平台下Liunx子系统 目前的目录: hello ├── manage.py ├── hello │ ├── _ ...

  9. Django 学习笔记(七)数据库基本操作(增查改删)

    一.前期准备工作,创建数据库以及数据表,详情点击<Django 学习笔记(六)MySQL配置> 1.创建一个项目 2.创建一个应用 3.更改settings.py 4.更改models.p ...

  10. Django 学习笔记(六)MySQL配置

    环境:Ubuntu16.4 工具:Python3.5 一.安装MySQL数据库 终端命令: sudo apt-get install mysql-server sudo apt-get install ...

随机推荐

  1. [AutoMapper]反射自动注册AutoMapper Profile

    AutoMapper 帮我我们方便管理物件跟物件之间属性值格式转换 模型转换 这里有两个类别 UserInfoModel 当作我们从DB捞取出来模型资料 public class UserInfoMo ...

  2. python的标识符

    1.在pyhon中,标识符由字母.数字.下划线组成 2.在python中,所有标识符可以有字幕,下划线开头,但不能以数字开头 3.python的标识符是区分大小写的 4.以下划线开头的标识符是有特殊意 ...

  3. nginx配置支持http2

    1.简介 nginx 配置支持http2.目前大多数网站都是http1.1(如果你没有特别配置过的话) 一切都是为了访问更快. 2.如何查看自己网站的http版本 最简单的方法就F12啊,我这里是火狐 ...

  4. QT之uic、moc、rcc命令生成相应的cpp文件

    1.rcc 生成qrc.cpp文件 2.uic生成ui_*.h,moc生成moc_*.cpp文件

  5. 胜利大逃亡,bfs,广度优先搜索

    题目描述: Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius ...

  6. redis cluster的conf配置文件配置

    redis cluster的conf配置文件配置 master配置文件如下: bind 127.0.0.1 port tcp-backlog timeout tcp-keepalive logleve ...

  7. python—函数

    # 函数:1.减少重复代码:# 2.使程序变的可扩展# 3.使程序变的易维护# 永远只能且返回一个值#def 定义内容# a,b = 5,8# c = a** b# print(c)## def ca ...

  8. 日常踩坑 — 相邻元素之间的margin合并问题。

    踩坑:使用v-for渲染的组件,当然图中的id已经换成class,还是没有解决这个问题,于是各种查找资料,我就不信简单的CSS问题这么难解决! v-for渲染组件级传值: <div class= ...

  9. python学习4---实现快速排序

    1.QuickSort def Rand_Partition(A,p,r): """ 划分数组的元素下标q :param A: 数组 :param p: 数组左边界 :p ...

  10. DashBoard创建各种表(一)

    创建透视表 1.首先需要创建一个DashBoard,然后点击开始导航栏中的透视,创建一个透视表,透视表可以交叉式的显示报告,让我们可以更直观的看到多维数据. 2.把CategroyName和Categ ...