Django的安全攻击
XSS
XSS(跨站脚本攻击)
是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。也属一种注入攻击,注入本质上就是把输入的数据变成可执行的程序语句比如这些代码包括HTML代码和客户端脚本。
危害
- 盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号
- 控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
- 盗窃企业重要的具有商业价值的资料
- 非法转账等
原理
-
反射型xss攻击
一般的浏览器与服务器交互都是,客户端向服务器发送一条请求,类
http://ww.bigshop.com/product/list/?q=手机
,也就是说客户向目标站点发送了一条查询所有手机的请求,然后服务器向客户端返回相关数据,这是正常的。并且手机这一关键词也会被重新返回给浏览器端的用户,这就使得xss攻击有机可乘。黑客将http://ww.bigshop.com/product/list/?q=
这条包含恶意脚本的链接通过某种方式发送给用户,那么用户一旦点击这条链接,就会向目标站点发送查询``的请求,这当然是查询不到任何结果,但是这段JavaScript的脚本就会从后台转了一圈又回到浏览器,这样浏览器就会执行这段代码,也就是是获取用户的cookie值。一旦黑客拿到你的cookie值,基本上就相当于可以模拟你的身份。
可以看到,攻击者巧妙地通过反射型XSS的攻击方式,达到了在受害者的浏览器上执行脚本的目的。由于代码注入的是一个动态产生的页面而不是永久的页面,因此这种攻击方式只在点击链接的时候才产生作用,这也是它被称为非持久型XSS的原因。 -
存储型xss攻击
存储型xss攻击与反射型xss攻击的区别在于,xss攻击已经存储在服务器的数据库上,可以长期的返回给浏览器端。例如当客户A在博客园的博客或者评论区写入一段js恶意脚本,这段脚本就会永久存储到博客园的数据库,一旦当有其他用户请求查看这条博客或者评论,博客园就会将相应的数据返回给用户,当然这段js恶意脚本也会被返回给用户的浏览器并执行。
防护
- 字符过滤:对用户的请求无论是url还是表单提交的内容都进行长度检查和对特殊字符进行过滤
- cookie方面:避免在cookie中放入重要敏感信息
- 表单提交:尽量使用post的方式提交表单而不是get方式
不仅服务器拿到用户的请求进行过滤,在服务器将数据返回给浏览器后,浏览器也会再一次进行防护。
django中自动的帮我们转义好了数据, 不需要手动转义
如果要给数据标记安全方式显示,有如下两中方法:
-
模版页面上对拿到的数据后写上safe. ----> {{XXXX|safe}}
-
在后台导入模块:from django.utils.safestring import mark_safe
把要传给页面的字符串做安全处理 ----> s = mark_safe(s)
csrf(Cross Site Request Forgery)
csrf(跨站域请求伪造)
CSRF 攻击允许恶意用户在另一个用户不知情或者未同意的情况下,以他的身份执行操作。
CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据 。黑客无法获取cookie和session值更无法进行解析。
过程
假如用户A在某电商网站注册过并且用户本地浏览器又存在网站服务端颁发给自己的通行证(也就是cookie),那么对于用户A来说自己访问网站并且提交请求购买商品都是畅通无阻,不需要身份验证。但是黑客B想要利用A的账户给自己买东西,用户B直接访问电商网站当然无法实现这一目的,原因是B没有A的通行证,想要利用A的账户在访问网站时又不知道A的密码。那么B自己建造了一个黑客网站,这个黑客网站中存在跳转向目标电商网站的黑客url链接,url链接又包含了用户的请求(假如是A购买了一个iphone X发给某地址,当然这是B想要的地址)。当用户访问黑客网站时,点击黑客链接时,目标网站就会接受到请求,由于该请求是A的浏览器发出的,所以又包含A的cookie,服务端就会将一个iphone X发往B的地址,那么黑客B的目的就达到了。所以在csrf中黑客B并不知道A的cookie,只是间接利用了cookie,拿着A的通行证瞒过目标网站,实现自己目的。
实现的方式:
- 黑客网页存在指向目标站点的链接,直接跳转
- 黑客网页包含动态加载的JavaScript,存在转向目标站点的动作。
- 黑客网页存在
https://www.cnblogs.com/welan/p/xxx
,其中xxx就是指向目标站点的链接,并且包含请求,例如src=”<http://goumai.example/withdraw?account=A&goods=iphonex&for=B> ”
Django 提供的 CSRF 防护机制
django 第一次响应来自某个客户端的请求时(get方式),会在服务器端随机生成一个 token,然后把这个 token 写在用户请求的 cookie 里,同时也会给客户端页面发送一个随机的 token (form表单中以{% csrf_token %}方式获取)用以认证。之后客户端每次以 POST 方式向服务端提交请求时,都会带上这个 token,这样就能避免被 CSRF 攻击。
- 在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token;
- 在所有的 POST 表单中,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成,见下面)
- 在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
- 在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值
Django 里如何使用 CSRF 防护
-
首先,最基本的原则是:GET 请求不要用!有副作用!也就是说任何处理 GET 请求的代码对资源的访问都一定要是“只读“的;
-
其次,要启用
django.middleware.csrf.CsrfViewMiddleware
这个中间件; -
最后,在所有的 POST 表单元素中,需要加上一个
{% csrf_token %}
tag。原因:用于csrf在模版渲染过程中,会自动为表单添加一个名为
csrfmiddlewaretoken
, type属性为 hidden 的 input。
# 1. 开启全局的csrf验证
'''
1. settings中,打开注释 'django.middleware.csrf.CsrfViewMiddleware'
2. 表单中,开启csrf_token
'''
<form>
{% csrf_token %}
<input type='text'>
</form>
# 2. 开启全局的csrf,但是部分业务函数不进行检验
'''
1. settings中,打开注释 ====》'django.middleware.csrf.CsrfViewMiddleware'
2. views中,引入如下函数
'''
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def csrf1(request):
if request.method == 'GET':
return render(request, 'csrf1.html')
else:
return HttpResponse('ok')
# 3. 关闭全局的csrf, 但是部分业务函数要使用csrf验证
'''
1. settings中,注释 ===》 #'django.middleware.csrf.CsrfViewMiddleware'
2. views中,引入如下函数
'''
- FBV方式
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf1(request):
if request.method == 'GET':
return render(request, 'csrf1.html')
else:
return HttpResponse('ok')
- CBV
from django.utils.decorators import method_decorator
@method_decorator(csrf_protect, name='get')
class User(View):
def get(self, request):
pass
def post(self, request):
pass
'''
ajax方式提交数据:
<form method="POST" action="/csrf1.html/">
{% csrf_token %}
<input id="user" type="text" name="user" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
1. 将token放置到请求头中, 携带过来
var token = $.cookie('csrftoken');
var user = $('#user').val();
$.ajax({
type:'POST',
url: '/csrf.html/',
headers : {'X-CSRFToken': token}
data:{'user':user},
})
2. 放置在data中携带
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
type:'POST',
url: '/csrf.html/',
data:{'user':user,'csrfmiddlewaretoken': csrf},
})
'''
sql注入攻击
原理
什么是sql注入
所谓SQL注入就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串(注入本质上就是把输入的字符串变成可执行的程序语句),最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。在Web应用漏洞中,SQL Injection 漏洞的风险要高过其他所有的漏洞。
相关原理
根据相关技术原理,SQL注入可以分为平台层注入和代码层注入。前者由不安全的数据库配置或数据库平台的漏洞所致;后者主要是由于程序员对输入未进行细致地过滤,从而执行了非法的数据查询。
表现
SQL注入的产生原因通常表现在以下几方面:
- 不当的类型处理;
- 不安全的数据库配置;
- 不合理的查询集处理;
- 不当的错误处理;
- 转义字符处理不合适;
- 多个提交处理不当。
案例
常见的就是进行登录验证的时候,表单提交的数据包含sql语句,代码如下
# login.html
<h1>欢迎登陆<h1>
<from method='post' >
<input type='text' name='username' >
<input type='text' name='password'>
<input type="submit" value="立即登录" >
</form>
# views.py
from django.shortcuts import render, HttpResponse, redirect
from django.views.generic.base import View
class LoginNotSafeView(View):
def get(self, request):
return render(request, 'login.html')
def post(self, request):
user_name = request.POST.get("username", "")
pass_word = request.POST.get("password", "")
import pymysql
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
db='mxonline',
charset='utf8' )
cursor = conn.cursor()
# 黑客可通过user或者password输入数据库语句对数据非法利用
sql_select = " select * from users_userprofile where username='{0}' and passworf='{1}' ".format(user_name, pass_word)
result = cursor.execute(sql_select)
for i in cursor.fetchall():
# 数据库所有查询结果
pass
-
如果想
username
框中填写admin
,password
为123
上面
sql_select = " select * from users_userprofile where username='{0}' and passworf='{1}'
就相当于
select * from users_userprofile where username=admin and passworf=123
进行查询 -
如果想
username
框中填写admin or admin=admin #
,password
为123
上面sql_select = " select * from users_userprofile where username='{0}' and passworf='{1}'
就相当于
select * from users_userprofile where username=admin or admin = admin # and passworf=123
进行查询
这里的#相当于把后面的所有查询包括password查询给注释,并且or admin = admin
的查询永远是正确的,所以sql攻击注入就完成了
防范
- 对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双"-"进行转换等。
- 不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
- 不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
- 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。