接口安全测试--sql注入
开放式Web应用程序安全项目(OWASP)发布的top10 Web应用软件安全风险榜单中,注入漏洞常年霸榜(参见:https://owasp.org/www-project-top-ten/ ),而在api接口测试中,sql注入是最常见的注入类型,也是非常容易被使用的攻击方式,并且其产生的危害非常大。
那么什么是sql注入?如何进行sql注入的测试呢?我们仍然通过实例来分析
1. 字符型sql注入
我们以前面章节《接口进阶》部分中的代码为基础,修改其中登录部分的代码,将写死在代码中的用户名和密码修改为从数据库中读取,为了便于说明问题,我们将实际执行的sql语句以结果返回,代码如下:
@app.route('/login',methods= ["POST"]) def login(): inp_name = request.form.get('username') inp_password = request.form.get('password') sql = "SELECT * FROM users WHERE name = '%s' AND password = '%s'"%(inp_name, inp_password) cursor = _db.cursor() rows = cursor.execute(sql) if rows: token = create_token(inp_name,inp_password) return jsonify(code=4,msg="登录成功",token=token.decode(),sql=sql) else: return jsonify(code=2,msg="非法用户") _cursor.close()
同时,我们在数据库中创建表users,初始化4条用户信息如下:
# # Structure for table "users" # CREATE TABLE `users` ( `Id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; # # Data for table "users" # INSERT INTO `users` VALUES (1,'admin','5690dddfa28ae085d23518a035707282'),(2,'Mr.null','dc483e80a7a0bd9ef71d8cf973673924'),(3,'Zhangsan','5690dddfa28ae085d23518a035707282'),(4,'Lisi','dc483e80a7a0bd9ef71d8cf973673924');
我们再次通过post请求,做如下case测试:
case1:用户名1、密码均正确
接口参数输入:
- username:admin
- password:5690dddfa28ae085d23518a035707282
预期结果:登录成功,并返回sql语句及token
实际结果:
{ "code": 4, "msg": "登录成功", "sql": "SELECT * FROM users WHERE name = 'admin' AND password = '5690dddfa28ae085d23518a035707282'", "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU5MDM3NTc3NCwiZXhwIjoxNTkwNDYyMTc0fQ.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiI1NjkwZGRkZmEyOGFlMDg1ZDIzNTE4YTAzNTcwNzI4MiJ9.LSQF19VXXPA376SOHVjA7ndWDK1GHcU8xYUJ4bYszoYui54GQm5ymW0BhMdIun1UfCMWF2AkNUAwDofMm-mLOg" }
测试结论:通过
输入参数中的password参数值为已经加密后的密文
case2:用户名1正确、密码错误
接口参数输入:
- username:admin
- password:dc483e80a7a0bd9ef71d8cf973673924
预期结果:非法用户
实际结果:
{ "code": 2, "msg": "非法用户" }
测试结论:通过
case3:用户名错误、密码正确
接口参数输入:
- username:admin1
- password:5690dddfa28ae085d23518a035707282
预期结果:非法用户
实际结果:
{ "code": 2, "msg": "非法用户" }
测试结论:通过
case4:用户名2正确、密码正确
接口参数输入:
- username:Mr.null
- password:dc483e80a7a0bd9ef71d8cf973673924
预期结果:登录成功,并返回sql语句及token
实际结果:
{ "code": 4, "msg": "登录成功", "sql": "SELECT * FROM users WHERE name = 'Mr.null' AND password = 'dc483e80a7a0bd9ef71d8cf973673924'", "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU5MDM3NjQxOSwiZXhwIjoxNTkwNDYyODE5fQ.eyJ1c2VybmFtZSI6Ik1yLm51bGwiLCJwYXNzd29yZCI6ImRjNDgzZTgwYTdhMGJkOWVmNzFkOGNmOTczNjczOTI0In0.mDI-B6NMnlx4s0iybU7VCeZ6IFnZEeC4CeI2Q0XuBmu-ZXrZl8pOBmZgp8AbprH0rrL9qJJqECjNWbinjZWA3g" }
测试结论:通过
上述1、4两个case分别用两组正确的用户名和密码登录系统,2、3分别是用户名或密码错误,登录失败,预期结果与实际结果均一致,测试通过,证明接口功能被正确实现,只有正确的用户名和密码才能登录系统,接下来我们设计一个特殊的case,来验证如果用户名后面增加某个特殊字符的组合,密码任意的情况下,系统能否登录成功。
case5:用户名'#、密码任意
接口参数输入:
- username:Mr.null'#
- password:
预期结果:非法用户
实际结果:
{ "code": 4, "msg": "登录成功", "sql": "SELECT * FROM users WHERE name = 'Mr.null'#' AND password = 'dc483e80a7a0bd9ef71d8cf973673924'", "token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU5MDM3NjY2NCwiZXhwIjoxNTkwNDYzMDY0fQ.eyJ1c2VybmFtZSI6Ik1yLm51bGwnICMiLCJwYXNzd29yZCI6ImRjNDgzZTgwYTdhMGJkOWVmNzFkOGNmOTczNjczOTI0In0.O1hCWOTKrdRkItVjeFmqb8uKcjKk7P2qhPFW-Vx21kEXiIx22DiH6VOVv9OoT6sfpc4umR-rj-Qzf14I5HeUgQ" }
实际结果与预期结果不一致,所以
测试结论:不通过
为什么用户名后加个"'#",且密码为空,还能登录成功呢?关键就在用户名后面跟着的字符上,我们将上面返回结果中的sql拿出来如下:
SELECT * FROM users WHERE name = 'Mr.null'#' AND password = 'dc483e80a7a0bd9ef71d8cf973673924'
通过sql语句的格式化显示,可以很清楚的看到"#"后面的条件语句被注释,所以并未生效,而"'"闭合了sql语句,使得sql语句是一个完整、正常、没有语法错误的语句,只要'Mr.null'这个用户名存在于数据库表中,sql语句就可以查询到结果,因而登录成功,这个就是sql注入的一个典型例子,上面的用户名如果输入"Mr.null'-- "(注意:MySQL数据库注释'--'后面需要带一个空格符' ',)也能达到同样的效果,需要注意的是,不同的数据库对sql注释的方式存在差异,以上注入方式仅针对MySQL数据库。
2.数字型sql注入
我们增加一个news接口,通过传入newsid参数的值,获取对应的news信息,代码如下:
@app.route('/news',methods= ["GET"]) def news(): newsid = request.args.get("newsid") sql = "SELECT * FROM news WHERE id = %s"%(newsid) cursor = _db.cursor() rows = cursor.execute(sql) data = cursor.fetchall() print(rows) if rows: return jsonify(code = 5, msg = "查询成功",sql = sql, count = rows, data = data) else: return jsonify(code = 6, msg = "未查询到数据", sql = sql) _cursor.close()
在数据库中创建表news,初始化3条信息如下:
# # Structure for table "news" # CREATE TABLE `news` ( `Id` int(11) NOT NULL AUTO_INCREMENT, `newsinfo` varchar(255) DEFAULT NULL, PRIMARY KEY (`Id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; # # Data for table "news" # INSERT INTO `news` VALUES (1,'info1'),(2,'in
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> 本专刊共五章 21 篇正文及 5 个对应的 GitHub 项目,主要介绍了接口测试基础知识及测试用例设计方法,认证鉴权、加密验签,自动化测试,安全性测试,性能测试等内容,每一章节都有代码实例来剖析其原理,并提供了一个完整电商系统的代码,可以直接实操演练,让你知其然知其所以然,轻松搞定接口测试。加入专刊,你就加入了一大群志同道合的优质测试人圈子,同时还有和作者及作者的朋友们互动交流的机会。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p> <p> <br /> </p>