Django 之安全篇
一、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_token
和body
中的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 token
的input
标签:{% 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相关知识:
iframe
可以加载嵌入别的域名下的网页。也就是说可以发送跨域请求。比如我可以在我自己的网页中加载百度的网站,示例代码如下:<iframe src="http://www.baidu.com/">
</ifrmae>
- 因为
iframe
加载的是别的域名下的网页。根据同源策略,js
只能操作属于本域名下的代码,因此js
不能操作通过iframe
加载来的DOM
元素。 - 如果
ifrmae
的src
属性为空,那么就没有同源策略的限制,这时候我们就可以操作iframe
下面的代码了。并且,如果src
为空,那么我们可以在iframe
中,给任何域名都可以发送请求。 - 直接在
iframe
中写html
代码,浏览器是不会加载的。
二、XSS攻击
XSS攻击
XSS(Cross Site Script)攻击又叫做跨站脚本攻击。他的原理是用户在使用具有XSS
漏洞的网站的时候,向这个网站提交一些恶意的代码,当用户在访问这个网站的某个页面的时候,这个恶意的代码就会被执行,从而来破坏网页的结构,获取用户的隐私信息等。
XSS攻击场景:
比如A网站
有一个发布帖子的入口,如果用户在提交数据的时候,提交了一段js
代码比如:<script>alert("hello world");</script>
,然后A网站
在渲染这个帖子的时候,直接把这个代码渲染了,那么这个代码就会执行,会在浏览器的窗口中弹出一个模态对话框来显示hello world
!如果攻击者能成功的运行以上这么一段js
代码,那他能做的事情就有很多很多了!
XSS攻击防御:
如果不需要显示一些富文本,那么在渲染用户提交的数据的时候,直接进行转义就可以了。在
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')
如果对于用户提交上来的数据包含了一些富文本(比如:给字体换色,字体加粗等),那么这时候我们在渲染的时候也要以富文本的形式进行渲染,也即需要使用
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'))
相关介绍如下:
tags
:表示允许哪些标签。attributes
:表示标签中允许哪些属性。ALLOWED_TAGS
:这个变量是bleach
默认定义的一些标签。如果不符合要求,可以对其进行增加或者删除。ALLOWED_ATTRIBUTES
:这个变量是bleach
默认定义的一些属性。如果不符合要求,可以对其进行增加或者删除。
三、点击劫持攻击
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
可以设置以下三个值:
DENY
:不让任何网页使用iframe
加载我这个页面。SAMEORIGIN
:只允许在相同域名(也就是我自己的网站)下使用iframe
加载我这个页面。ALLOW-FROM origin
:允许任何网页通过iframe
加载我这个网页。
在Django
中,使用中间件django.middleware.clickjacking.XFrameOptionsMiddleware
可以帮我们堵上这个漏洞,这个中间件设置了X-Frame-Option
为SAMEORIGIN
,也就是只有在自己的网站下才可以使用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
语句实现以下需求:
实现一个根据用户
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
表中所有的数据都提取出来。实现一个根据用户的
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')
这样表面上看起来也没有问题。但是如果用户传的
username
是zhiliao' 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
注入呢?归类起来主要有以下几点:
- 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
- 永远不要使用动态拼装
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')
- 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
- 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
- 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装。
在Django中如何防御sql
注入:
- 使用
ORM
来做数据的增删改查。因为ORM
使用的是参数化的形式执行sql
语句的。 - 如果万一要执行原生
sql
语句,那么建议不要拼接sql,而是使用参数化的形式。
Django 之安全篇的更多相关文章
- 安全篇:弱密码python检测工具
安全篇:弱密码python检测工具 https://github.com/penoxcn/PyWeakPwdAudit
- 7-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(GPRS模块SSL连接MQTT)
6-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(Wi-Fi模块SSL连接MQTT) 由于GPRS是直接和GPRS基站进行连接,其实对于GPRS而言,即使不加 ...
- 6-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(Wi-Fi模块SSL连接MQTT)
5-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(配置MQTT的SSL证书,验证安全通信) 首先确定自己的固件打开了SSL,升级篇里面的固件我打开了SSL,如 ...
- 公有云Docker镜像P2P加速之路:安全篇
一.问题 在使用Docker运行容器化应用时,宿主机通常先要从Registry服务(如Docker Hub)下载相应的镜像(image).这种镜像机制在开发环境中使用还是很有效的,团队成员之间可以很方 ...
- 再探.NET的PE文件结构(安全篇)
一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...
- Web安全攻防-----TCP/IP安全篇
知识点: 掌握TCP/IP的体系分层结构 掌握TCP/IP的各一层功能特点 掌握TCP/IP的数据在各层的名称 掌握TCP/IP的体系数据的封装和解封装 1.TCP/IP协议的历史 TCP/IP的起源 ...
- http安全篇
一.app与服务端交互确保来源的安全 作为一个移动互联网App,天生是需要和服务器通信的.那么,服务器如何识别客户端的身份?我们如何保证数据传输过程中的安全性?要靠两个东西:使用AppKey做身份识别 ...
- linux安全篇
笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 1.限制用户su 限制能su到root的用户. 操作步骤 使用命令 vi /etc/pam.d/su修改配置文件, ...
- web开发性能优化---安全篇
1.权限管理 从模块.表单.数据审核.功能按钮全面数据安全验证及管理. 2.ip验证 数据接口访问进行IP校验 3.登录.操作日志.程序安全日志 系统所有用户登录.操作全部日志记录. 程序安全日志操 ...
随机推荐
- HTML5类操作
一.获取DOM的方式 ①通过类名获取元素,以伪数组形式存在 document.getElementsByClassName("class"); ②通过css选择器获取元素,符合匹配 ...
- 洛谷 P1004 方格取数 题解
P1004 方格取数 题目描述 设有 \(N \times N\) 的方格图 \((N \le 9)\),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字\(0\).如下图所示(见样例): ...
- POJ 1927 Area in Triangle
Area in Triangle Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 1674 Accepted: 821 D ...
- 微信小程序--安装教程
首先 奉上腾讯官方文档 方便参考:https://mp.weixin.qq.com/debug/wxadoc/design/index.html 个人认为没说啥特别有用的信息(可能是我看的太粗糙了) ...
- linux服务器时间乱码问题解决
问题现象如下: [root@ip-171-21-36-129 testcase]# date 2019Ū 08Ղ 02ɕ чǚϥ 09:44:48 UTC 解决步骤: 1.执行命令:vi /etc/s ...
- <c:choose>
备注一下属性 DIV没有VALUE属性 <c:choose> <c:when test="${yggModel.type=='0'}">食品< ...
- js中引入js
第一个js文件(被引入的js文件),文件名one.js,内容如下 function alertInOne(){ alert('in one');} 第二个js文件,文件名two.js,内容如下 ...
- Mininet系列实验(七):Mininet脚本实现控制交换机行为
1 实验目的 熟悉Mininet自定义拓扑脚本的编写: 掌握使用“ovs-vsctl”命令直接控制Open vSwitch. 2 实验原理 在SDN环境中,控制器可以通过对交换机下发流表操作来控制交换 ...
- UK Biobank专题
这个时代的生信,统计遗传,不懂或不会用这个数据库就说不过去了. 看看10年GWAS里是如何定位和评价UK biobank的: For the near future, the UK Biobank i ...
- BeanDefinitionParserDelegate与资源解析
继续上一次的分析 XmlBeanDefinitionReader.java 中 1.registerBeanDefinitions方法 documentReader.registerBeanDefin ...