XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
模拟CSRF攻击案例:余额怎么变少了?
小王和小李是某ICBC银行的2个正常的用户,用户小黑是一名黑客,也在该银行开了银行账户.三个用户的余额情况如下:
某天,小王给小李转账2000元成功后,收到了黑客小黑发了一个链接
小王打开连接后,显示的是一个幽默的大图:是男人就坚持600秒
万万没想到的是,小王银行卡里的钱少了100,更邪门的是,每打开一次这个界面,就会少100元!
小王一共点击了该页面10次,累计损失了1000元.
这是为何?
原来该icbc银行网站并没有开启csrf的校验,该位置位于settings.py的
MIDDLEWARE
下面django.middleware.csrf.CsrfViewMiddleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', # 安全相关的中间件 比如:预防XSS攻击处理
'django.contrib.sessions.middleware.SessionMiddleware',# session中间件
'django.middleware.common.CommonMiddleware', # 自动给URL加斜杠和www的,等一些其他的小细节处理
# 'django.middleware.csrf.CsrfViewMiddleware', # csrf攻击的中间件
'django.contrib.auth.middleware.AuthenticationMiddleware', # 添加user这样一个属性,当然不仅仅这些,是一个授权的中间件
'django.contrib.messages.middleware.MessageMiddleware', # message中间件
'django.middleware.clickjacking.XFrameOptionsMiddleware', # 防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现。
'account.middleware.mymiddleware.UserMiddleware'
]
启用该中间件,同时在输入的表单中,加入
{% csrf_token %}
<form action="" method='POST'>
{% csrf_token %}
<table>
<tbody>
<tr>
<td><label for="">目标用户:</label></td>
<td><input type="text" name='username'></td>
</tr>
<tr>
<td><label for="">转账金额:</label></td>
<td><input type="text" name='money'></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value='确定'></td>
</tr>
</tbody>
</table>
{% if errors %}
<p>{{errors}}</p>
{% endif %}
</form>
从那之后,无论小王再怎么不是男人,点开是男人坚持600秒的 那个钓鱼站点,都不会再损失钱了.
1.CSRF跨站攻击原因分析
-
用户C在访问icbc站点A的时,登录完成后,A生成cookie交给C
-
C没有退出的情况下,访问了站点B
-
B在C没有退出登录的情况下,拿着C的cookie去访问A,进行转账操作.
-
由于C并没有退出登录,A执行了B伪造C的请求,所以完成了转账.
分析该网站的源代码可以看到,转账操作其实是使用post方式提交了一个包含了用户名username,金额money的表单到当前的页面.
而钓鱼网站刚好是利用了这点漏洞,通过使用iframe隐藏框架,下挂一个隐藏的表单,这个表单跟银行转账的表单相似,参数一样,提交的内容一样.通过js脚本控制,加载该页面后,自动提交该表单的转账给小黑100元的请求.
小王又正好在登录状态下,点开了这个网站.登录银行的会话sessionid依然存在.
钓鱼网站恰好利用了该漏洞,进行跨域攻击.利用的是未退出的会话状态和未进行csrf防范的漏洞网站.
当网站开启了csrf校验后,每次向用户发送的页面,都带有csrfmiddlewaretoken这个中间件的跨站伪造校验码.每一次提交表单,都会将这个校验码回传,如果回传的校验码不正确,那么这个提交的操作将会被拒绝.达到了跨站攻击防御的目的.因为钓鱼网站没有得到csrf_token,无法通过验证.
相关代码下载http://test.gxticket.com:8080/pic/?name=csrf.rar
2.装饰器的用法
上个例子中,转账之前使用了装饰器login_required来检查是否已经登陆,那么是如何实现的呢?
在当前应用目录下面的decorators.py,用来存放我们需要使用的装饰器函数.
#coding: utf-8
from models import AccountModel
from django.shortcuts import redirect,reverse
login_required
def login_required(func): #装饰器名称是login_required
def wrapper(request,*args,**kwargs): #内部定义个wrapper函数,传入request,和其他参数,wrapper是包装,封装的意思
username = request.session.get('username',None) #通过传入的request从服务器备份的request.session中获取username.
account = AccountModel.objects.filter(username=username).first() #查询数据库用户名是否存在,返回第一条记录对象
if account: #如果存在,将传入的函数原封不动的返回
return func(request,*args,**kwargs)
else:
return redirect(reverse('login')) #否则跳转到登陆界面
return wrapper
3.中间件(Middleware)
3.1什么是中间件?
-
中间件是在request和view之间以及view到response之间做的一些处理。
-
使用中间件要注意放的顺序。通常是按照顺序执行.
#django常见的中间件及相关解释
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', # 安全相关的中间件 比如:预防XSS攻击处理
'django.contrib.sessions.middleware.SessionMiddleware',# session中间件
'django.middleware.common.CommonMiddleware', # 自动给URL加斜杠和www的,等一些其他的小细节处理
'django.middleware.csrf.CsrfViewMiddleware', # csrf攻击的中间件
'django.contrib.auth.middleware.AuthenticationMiddleware', # 添加user这样一个属性,当然不仅仅这些,是一个授权的中间件
'django.contrib.messages.middleware.MessageMiddleware', # message中间件
'django.middleware.clickjacking.XFrameOptionsMiddleware', # 防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现。
'account.middleware.mymiddleware.UserMiddleware' #用户自定义的中间件
]
3.2中间件的实现过程
– 前台发起http请求过来是,首先要经过一些列的中间件
例如CommonMiddleware自动给URL加斜杠和www的,等一些其他的小细节处理,SessionMiddleware’,# 进行会话处理,CsrfViewMiddleware进行csrf攻击的处理,AuthenticationMiddleware认证处理,MessageMiddleware消息处理
– 经过这些中间件,其实类似于调用了一个有一个的函数去过滤.才到达views视图函数处理
– 处理完毕后,中间件也可以参与response的响应
3.3自定义中间件:
在应用目录下创建一个专门存放中间件的文件夹middleware,然后在里面包含一个init.py文件用来表示这是一个包,然后创建一个中间件的文件mymiddleware.py,用来存放指定的中间件:
#coding: utf8
from account.models import AccountModel
from django.utils.deprecation import MiddlewareMixin #需要从django.utils.deprecation导入MiddlewareMinxin模块
# 1.10版本之后的中间件
def UserMiddleware(get_response): #定义一个用户自定义的UserMiddleware中间件,传一个系统函数get_response作为参数
def middleware(request):
# request中间件
username = request.session.get('username',None) #从ression取出是否有该用户,如果没有则为空
user = AccountModel.objects.filter(username=username).first() #检查数据库是否有该用户存在
print 'middleware first'
if user and not hasattr(request,'frontuser'): #弱用户存在于数据库,且用户没有设置属性frontuser
setattr(request,'frontuser',user) #对该用户设置frontuser属性,并返回.
response = get_response(request)
# response中间件
return response
return middleware
#1.10之前是封装成类
使用方法:在视图函数中,如果访问主页,已登录用户,具有frontuser属性,打印—,未登陆用户,打印++++
def index(request):
if hasattr(request,'frontuser'): #是否有frontuser属性,有则说明已经登陆.这个属性就是上面提到的中间件添加的.
print '------------------'
else:
print '++++++++++++++++++'
return render(request,'index.html',{'user':'','comment':''})
使用中间件后查看是否登陆的效果.
总结
通过ICBC转账的一个实例,引出了CSRF的原理和分析,并提供了Django设置csrf的方法做了详细介绍说明.同时介绍了自定义装饰器的定义和用法,中间件的理论,以及如何自定义中间件.请重点掌握.
最新评论