2.2 CSRF注入
跨站请求伪造(Cross-Site Request Forgery:CSRF),也被称为 One-Click Attack 或者 Session Riding,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。与跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
1. CSRF注入介绍
下面是一个典型的CSRF注入。
URL:http://www.mydomain.com/pay.jsp?user=Jerry&pay=100,表示Jerry给当前用户转了100元。那么黑客登录系统,构造这么一个URL:http://www.mydomain.com/pay.jsp?user=Tom&pay=10000000,表示Tom给当前用户(黑客)转了10,000,000元。
2. CSRF注入分类及攻击方法
CSRF注入可以分为GET注入和POST注入。
1)GET注入
在CSRF注入介绍介绍的就是GET注入,再看下面一个例子。
隐式链接
<ahref="http://www.mydomain.com/del_paper.jsp?id=5">删除</a>
显式链接
<imgscr=http://www.mydomain.com/del_paper.jsp?id=5 width="0"height="0">
均表示删除编号为5的文章。黑客可以通过Python的requests类编写接口代码进行攻击。
for i in range (10000):
payload={id:str(i)}
url= "http://www.mydomain.com/del_paper.jsp"
data =requests.get(url,params=payload)
攻击以后,系统中编号0到10000的文章都被删除。
2)POST注入
以下为一个程序的登录代码
<formclass="form-signin" method="post" action="/login_action/"onsubmit=javascript:check()>
<p>用户名: <input type="text"name="username" maxlength="100" requiredid="id_username"></p>
<p>密码 :<inputtype="password" name="password" requiredid="id_password"></p>
<input type= "submit" value= "提交" requiredid="id_password">
</form>
黑客可以通过Python的requests类编写接口代码,建立存放用户名和密码的文件:a.cvs,使用暴力攻击的办法进行破解。
在本地建立登录HTML,form的action改为绝对路径。
<formclass="form-signin" method="post" action="http://www.yourdomain.com/login_action/"onsubmit=javascript:check()>
<p>用户名: <input type="text"name="username" maxlength="100" requiredid="id_username"></p>
<p>密码 :<inputtype="password" name="password" requiredid="id_password"></p>
<input type= "submit" value= "提交" requiredid="id_password">
</form>
对这个代码建立对应的接口测试代码。
while True:
line=f.readline()
ifnot line:
break
username=line[0:line.rfind(',')]
password=line[line.rfind(','):]
self.driver.find_element_by_id("id_username").clear()
self.driver.find_element_by_id("id_username").send_keys(username)
self.driver.find_element_by_id("id_password").clear()
self.driver.find_element_by_id("id_password").send_keys(password)
self.driver.find_element_by_class_name("form-signin").submit()
如果a.cvs存在系统中的账号,暴力破解成功,否则失败。
3. CSRF注入测试方法
CSRF注入可以用CSRFTester工具进行测试,详细请参见本书下篇6.2.1节。
4. CSRF注入防护方法
1)CSRF Token技术
CSRF Token技术是在页面产生GET或POST请求之前,建立一个参数,以及一个cookie,参数的值与cookie的值是相等的,当HTTP请求传输到服务器端的时候,服务器会检查GET或POST请求参数是否与cookie的值相等,如果相等返回200代码,否则返货403代码。
下面介绍下Django中的django.middleware.csrf.CsrfViewMiddleware的工作原理。比如登录页面路径为/login_form/,登录成功页面路径为/login_action/。/login_form/的HTML代码为。
…
<form action="/login_action/">
<inputtype="hidden" value="1287A5666…"name="csrfmiddletoken">
…
</form>
…
在发送这个页面的同时,发送了一个cookie,内容为:{csrftoken:"1287A5666…"}。当HTML请求页面发送到服务器端,服务器进行验证名为csrfmiddletoken hidden中的内容与名为csrftoken的cookie内容是否相同,如果相同,返回200(OK)响应码,然后进入/login_action/,否则返回403(Forbidden)响应码。如13所示。
13 CSRF Token技术
ESAPI提供了ESAPI.httpUtilities().getCSEFToken来获取token值,通过调用ESAPI.randomizer().getRandomString(8,EncoderConstants.CHAR_ALPHANUMERICS)来生成CSRF Token值。
但是现在许多企业采用了前后端分离技术,前端处理HTTP渲染交互,后端负责向前端与数据库交互数据及业务处理,甚至在中间加入一个以Node.js的中间层,目的是解决效率与跨域问题。这样采用CSRF Token就无能为力了,解决的办法是在服务器端加入一个特殊处理模块,用于传递验证Token。见14中服务器的其他模块。(14来源于参考文献XXX)。
14 CSRF Token在前后端分离中的解决方案
2)其他方法
但是这个方法是防君子不防小人的,有经验的工程师可以构造接口测试代码,将cooiles的值与hidden中的值设置为一样的,然后提交,可以通过假Token绕过检查,代码如下。
def setUp(self):
self.correctusername="cindy"
self.correctpassword="123456"
self.discorrectusername="jerry"
self.discorrectpassword="000000"
self.url="http://127.0.0.1:8000/login_action/"
self.token= "RNF3Y04qFeJkMwCDsTMn4gfMcyfQ2vUjXbcENLADEFyCSRp1pBdezZKwHhlSwqgE"
self.cookie ={"csrftoken":self.token}
#正确的用户名,正确的密码
def test_login(self):
payload={"username":self.correctusername,"password":self.correctpassword,"csrfmiddlewaretoken":self.token}
data = requests.post(self.url,data=payload,cookies=self.cookie)
#验证返回码
self.assertEqual("200",str(data.status_code))
#验证返回内容
self.assertIn("电子商务系统" ,str(data.text))
解决这个问题的办法是:在token的基础上再加上Origin、Referer属性确定请求源与目的源是不是同源。另外还可以通过二次认证、验证码等方式解决。