专栏首页用户6517667的专栏谈谈Django的CSRF插件的漏洞

谈谈Django的CSRF插件的漏洞

今年十月份我的第二本书《基于Django的电子商务网站设计》出版了,在这本书中我不仅介绍了如何利用Django框架搭建电子商务网站,也论述了如何利用python的requests类对所创建的电子商务产品进行接口测试。在书写极乐口测试代码过程中,我遇到的最大的困难就是如何通过测试程序绕过Django的防止CSRF攻击的插件,通过近一个多月的努力我终于解决了这个问题,但是同时也揭露了Django框架的防止CSRF攻击的插件的漏洞。首先我们来看一下什么是CSRF攻击。

1、什么是CSRF攻击?

我们假设一个网站http://www.a.com/login.html的HTML代码如下:

<html><head></head><body><h4>用户登录</h4><form name="form" action=" /login/"><p>用户名:<input id="name" type="text" maxlength="50"></p><p>密码:< input id="password" type="password" maxlength="50"></p><p>验证码:<img src="/images/csr.jpg"></p>< input id="submit" type="submit" value="提交"></body></html>

大家都知道,采用验证码的目的是为了防止“黑客”,利用机器来通过穷举的方法来试图登录系统。检查验证码是否正确用的往往是前端做的判断。这样,“黑客”可以采用自己的网站建立如下页面:

<html><head></head><body><h4>用户登录</h4><form name="form" action="http://www.a.com/login/"><p>用户名:<input id="name" type="text" maxlength="50"></p><p>密码:< input id="password" type="password" maxlength="50"></p>< input id="submit" type="submit" value="提交"></body></html>

大家可以看见,在这段代码中验证码没有了,form的action变成了绝对路径http://www.a.com/login/,这样“黑客”就绕过了前端的验证,可以对自己代码进行编写自动化脚本实现用穷举的方法来试图登录系统。这个就是CSRF攻击。

2、Django的CSRF插件是如何解决CSRF攻击的

下面让我们来看一下Django的CSR插件是如何解决CSRF攻击的。Django利用了一个名为django.middleware.csrf.CsrfViewMiddleware的中间件(可以在Django的settings.py中设置)利用CSRF令牌的方式来控制。具体方式生成一个一百个字符的随机字符串作为CSRF令牌,在login表单中产生一个名为csrfmiddlewaretoken的hidden表单,把这个CSRF令牌的值放入这个字段中,然后在提交这个表单的时候产生一个名为csrftoken的cookie,这个cookie的值也是CSRF令牌的值。

<html><head></head><body><h4>用户登录</h4><form name="form" action="http://www.a.com/login/"><input type='hidden' name='csrfmiddlewaretoken' value='Pxpy5PDU3i1imqd0XZrK4ct6pZRIknHT48UE60GRrKtmqW7UCPq66pddXp0fzTpx' /><p>用户名:<input id="name" type="text" maxlength="50"></p><p>密码:< input id="password" type="password" maxlength="50"></p>< input id="submit" type="submit" value="提交"></body></html>

后台检查如果hidden表单的值与csrftoken的cookie的值一致,则返回200返回码,进入登录后的页面,否则返回403返回码,拒绝进入系统。由于这个CSRF令牌是随机生成的一百个字符的字符串,“黑客”是很难猜到这个字符的,所以就达到了CSRF的攻击防护。

3、Django的CSRF插件的漏洞

3.1通过requests类破解

但是这个CSRF插件是有漏洞的,在页面login.html页面载入后,黑客可以通过某种手段(比如正则表达式)获得这个CSRF令牌(即hidden中的一百个字符值),然后构造一个名为csrftoken的cookie,名为刚才过的的CSRF令牌值,这样就有了下面的代码。

import requests…#进入登录页面 try: data = requests.get(self.Login_url) except Exception as e: print(e) text = data.text csrf_token = str(re.findall(r"name=\'csrfmiddlewaretoken\' value=\'(.+?)\' />",text)) csrf_token = csrf_token[2:-2] payload ={"username":"cindy","password":"123456","csrfmiddlewaretoken":csrf_token} cookies = {"csrftoken":csrf_token} try: data = requests.post(self.Product_list_url,data=payload,cookies=cookies) except Exception as e: print(e)

代码“csrf_token =str(re.findall(r"name=\'csrfmiddlewaretoken\' value=\'(.+?)\'/>",text))”是通过re.findall正则方法获得CSRF令牌,存在csrf_token变量中,由于用这个方法获得的值是“["CSRF令牌值"]”格式的,也就是说去前面多了个“["”,后面多了个“"]”,所以后面用语句“csrf_token = csrf_token[2:-2]”过滤出来,然后利用requests的post方法,先构造post参数:“payload={"username":"cindy","password":"123456","csrfmiddlewaretoken":csrf_token}”,这里“"csrfmiddlewaretoken":csrf_token”让表单csrfmiddlewaretoken仍旧为csrf_token值。通过cookies = {"csrftoken":csrf_token}构造cookes值,通过cookies=cookies作为post参数传给后台。这样表单csrfmiddlewaretoken的值与cookie的csrftoken值是一致的,所以,登录通过。

后来,我惊奇的发现不用这么麻烦,我们直接把表单csrfmiddlewaretoken的值与cookie的csrftoken值设置相同,即:

csrf_token = csrf_token = "Pxpy5PDU3i1imqd0XZrK4ct6pZRIknHT48UE60GRrKtmqW7UCPq66pddXp0fzTpx" payload ={"username":"cindy","password":"123456","csrfmiddlewaretoken":csrf_token} cookies = {"csrftoken":csrf_token} try: data = requests.post(self.Product_list_url,data=payload,cookies=cookies) except Exception as e: print(e)

3.2通过selenium框架破解

下面的代码是利用selenium做基于GUI的自动化测试代码。

def test_CheckLogin(self): … 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) csrftoken = self.driver.find_element_by_name("csrfmiddlewaretoken").get_attribute("value") self.driver.add_cookie({"name":"csrftoken","value":csrftoken}) self.driver.find_element_by_class_name("form-signin").submit() …

代码通过csrftoken =self.driver.find_element_by_name("csrfmiddlewaretoken").get_attribute("value")获取表单csrfmiddlewaretoken的值,通过elf.driver.add_cookie({"name":"csrftoken","value":csrftoken})把这个值放入到名为csrftoken的cookies中。

3.3通过JMeter破解

在JMeter也可以破解,如下图:

通过正则表达式提取器获取login.html中的hidden值。

把获得的值放入名为csrftoken的cookie中

把获得的值仍旧作为csrfmiddlewaretoken表单参数传给后台处理。

3.4通过LoadRunne破解

在LoadRunner中,录制完毕,脚本就直接把csrfmiddlewaretoken表单参数作为名为csrftoken的cookie传给后台,不用做任何代码修改。正是不可思议。

web_add_cookie("csrftoken=D7rghfxcDXMOPlz1txbY5KigHQJasLoW0cilXmpONF87D64JM2eb2qKULO4zGc8Z; DOMAIN=192.168.0.106");… web_submit_data("login_action", "Action=http://192.168.0.106:8000/login_action/", … "Name=csrfmiddlewaretoken", "Value=D7rghfxcDXMOPlz1txbY5KigHQJasLoW0cilXmpONF87D64JM2eb2qKULO4zGc8Z", ENDITEM, "Name=username", "Value={username}", ENDITEM, "Name=password", "Value={password}", ENDITEM, LAST);

本文分享自微信公众号 - 软件测试培训(iTestTrain),作者:顾翔

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何将性能测试与持续集成连接起来

    作者:Yuan_Jie 链接:https://www.jianshu.com/p/edc0d171a6d1 来源:简书 简书著作权归作者所有,任何形式的转载...

    小老鼠
  • 安全测试工具(连载10)

    apktool是一个为逆向工程师打造的用于反编译Android二进制APP的工具。它可以将资源解码为几乎原始的形式,并在修改之后重建它们。本书介绍的apktoo...

    小老鼠
  • 软件安全性测试(连载14)

    顾翔老师近期推出一对一入职面试辅导。有兴趣者可加微信xianggu19720625与我联系。先要提供简历初选,合适者进一步洽谈。

    小老鼠
  • 性能测试与持续集成(JMeter+Jenkins)

    目的 将性能测试与持续集成挂接起来 性能测试: JMeter 持续集成: Jenkins JMeter 下载JMeter,官网: http://jmeter.a...

    小小科
  • 【编程语言】Java虚拟机垃圾回收算法,2020年的面试你准备好了吗

    熟悉 Java 的朋友一定知道 Java 虚拟机了,熟练掌握 Java 虚拟机是一个高级工程师的基础素养哦,当然面试官在问到 Java 虚拟机的时候,一定会问到...

    kk大数据
  • 如何将性能测试与持续集成连接起来

    作者:Yuan_Jie 链接:https://www.jianshu.com/p/edc0d171a6d1 来源:简书 简书著作权归作者所有,任何形式的转载...

    小老鼠
  • 【手记】手机网页弹出层后屏蔽底层的滑动响应

    这个需求场景很常见,但好像到目前还没有一个正统的做法,以至于一搜这个问题,出来的招数五花八门,典型的包括:

    AhDung
  • 小朋友学Java(11):枚举

    在C/C++/Java中,数据类型可以分为两大类。 一类是基本类型,比如int, long, float, double, char, String等 另一类是...

    海天一树
  • 微信小程序开发 导入文件说没找到.json的问题

    ydymz
  • Maven编译又失败了!还进行持续集成?

    泽阳

扫码关注云+社区

领取腾讯云代金券