接口安全测试--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>

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务