SQL注入会引发什么问题?
SQL注入是一种对数据库的恶意攻击,注入进去的恶意指令就会被误认为是正常的SQL指令而执行,因此遭到破坏或是入侵。
什么是SQL注入?
SQL注入(英语:SQL injection),也称SQL注入或SQL注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而执行,因此遭到破坏或是入侵。
为什么会发生SQL注入?
在设计不良的应用程序中,对用户输入数据的合法性并没有判断或过滤不严导致。
![图片[1]-SQL注入介绍 - 个人知识库-个人知识库](https://donglf.xyz:9001/zskcloud/2022/03/%E5%9B%BE%E7%89%87-1.png)
如图中所示,没有对客户端用户输入的数据合法性进行检查或过滤,导致客户端用户可以任意构造自己想要的参数,达成SQL注入条件,最终引发严重的后果,如果在账号登录成功SQL注入,那么就可以成功登录他人的账号,使用对他人的账号进行一系列破环手段,比如黑客通过SQL注入成功登录你的微信,可以使用你微信里面的余额,给你的家人朋友发钓鱼链接等等。
-
资料表中的资料外泄,例如企业及个人机密资料,账户资料,密码等。
-
数据结构被黑客探知,得以做进一步攻击(例如SELECT * FROM sys.tables)。
-
数据库服务器被攻击,系统管理员账户被窜改(例如ALTER LOGIN sa WITH PASSWORD=’xxxxxx’).
-
获取系统较高权限后,有可能得以在网页加入恶意链接、恶意代码以及Phishing等。
-
经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统(例如
xp_cmdshell “net stop iisadmin”可停止服务器的IIS服务)。
-
攻击者利用数据库提供的各种功能操纵文件系统,写入Webshell,最终导致攻击者攻陷系统
-
破坏硬盘资料,瘫痪全系统(例如xp_cmdshell “FORMAT C:”)。
-
获取系统最高权限后,可针对企业内部的任一管理系统做大规模破坏,甚至让其企业倒闭。
网站主页被窜改,导致声誉受到损害。
-
总之作为程序设计者,需要保证程序的健壮性避免被SQL注入攻击。
如何避免SQL注入?
- 所有的查询语句都使用数据库提供的参数化查询(Parameterized Query)接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL语句中,当前几乎所有的数据库系统都提供了参数化 SQL语句执行接口,使用此接口可以非常有效的防止 SQL注入攻击。
set @name := xxx;set @pwd := xxx;select id from users where name = @name and pwd = @pwd
set @name := xxx; set @pwd := xxx; select id from users where name = @name and pwd = @pwd
set @name := xxx; set @pwd := xxx; select id from users where name = @name and pwd = @pwd
- 在组合SQL字符串时,先针对所传入的参数加入其他字符(对进入数据库的特殊字符('<>&*;等)进行转义处理)。
- 确认每种数据的类型,比如数字型的数据就必须是数字,数据库中的存储字段必须对应为int型。
try:pwd = int(param.get("pwd"))except (TypeError, ValueError):return "pwd type must be int"
try: pwd = int(param.get("pwd")) except (TypeError, ValueError): return "pwd type must be int"
try: pwd = int(param.get("pwd")) except (TypeError, ValueError): return "pwd type must be int"
-
数据长度应该严格规定,能在一定程度上防止比较长的SQL注入语句无法正确执行。
name_max_length = 12if len(param.get("name", "")) > name_max_length:return "name length cannot be greater than 12"name_max_length = 12 if len(param.get("name", "")) > name_max_length: return "name length cannot be greater than 12"
name_max_length = 12 if len(param.get("name", "")) > name_max_length: return "name length cannot be greater than 12"
- 网站每个数据层的编码统一,建议全部使用 UTF-8编码,上下层编码不一致有可能导致一些过滤模型被绕过。
- 严格限制网站用户的数据库的操作权限,给此用户提供仅仅能够满足其工作的权限,从而最大限度的减少注入攻击对数据库的危害
- 避免网站显示 SQL错误信息,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断。
案例
这里分析一个案例
1.数据库中先创建用户表及数据
-- 创建一张用户表CREATE TABLE `users` (`id` INT(11) NOT NULL AUTO INCREMENT,`username` VARCHAR(20),`password` VARCHAR(50),PRIMARY KEY (`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8;-- 插入数据INSERT INTO users(username,`password`) VALUES('张三','456123'),('李四','qqatfv'),('王五','Qwe123');INSERT INTO users(username,`password`) VALUES('小张','987456'),('小王','ngjplg'),('小李','!@#$%^');-- 查看数据SELECT * FROM users;+----+----------+----------+| id | username | password |+----+----------+----------+| 1 | 张三 | 456123 || 2 | 李四 | qqatfv || 3 | 王五 | Qwe123 || 4 | 小张 | 987456 || 5 | 小王 | ngjplg || 6 | 小李 | !@#$%^ |+----+----------+----------+6 rows in set (0.00 sec)-- 创建一张用户表 CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO INCREMENT, `username` VARCHAR(20), `password` VARCHAR(50), PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 插入数据 INSERT INTO users(username,`password`) VALUES('张三','456123'),('李 四','qqatfv'),('王五','Qwe123'); INSERT INTO users(username,`password`) VALUES('小张','987456'),('小 王','ngjplg'),('小李','!@#$%^'); -- 查看数据 SELECT * FROM users; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | 张三 | 456123 | | 2 | 李四 | qqatfv | | 3 | 王五 | Qwe123 | | 4 | 小张 | 987456 | | 5 | 小王 | ngjplg | | 6 | 小李 | !@#$%^ | +----+----------+----------+ 6 rows in set (0.00 sec)-- 创建一张用户表 CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO INCREMENT, `username` VARCHAR(20), `password` VARCHAR(50), PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; -- 插入数据 INSERT INTO users(username,`password`) VALUES('张三','456123'),('李 四','qqatfv'),('王五','Qwe123'); INSERT INTO users(username,`password`) VALUES('小张','987456'),('小 王','ngjplg'),('小李','!@#$%^'); -- 查看数据 SELECT * FROM users; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | 张三 | 456123 | | 2 | 李四 | qqatfv | | 3 | 王五 | Qwe123 | | 4 | 小张 | 987456 | | 5 | 小王 | ngjplg | | 6 | 小李 | !@#$%^ | +----+----------+----------+ 6 rows in set (0.00 sec)
2.编写一个登录程序
import pymysqldef login():# 打开数据库连接db = pymysql.connect(host='127.0.0.1', port=3306, user='root',passwd='12345', db='test', charset='utf8')# 使用 cursor() 方法创建一个游标对象 cursorcursor = db.cursor()username = input('请输入用户名:')password = input('请输入密码:')sql = "select * from users where username = '%s' and password ='%s'" % (username, password)print(sql)# 执行SQL语句cursor.execute(sql)results = cursor.fetchone()if results:print('登录成功')else:print('登录失败')# 关闭数据库连接db.close()import pymysql def login(): # 打开数据库连接 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='12345', db='test', charset='utf8') # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() username = input('请输入用户名:') password = input('请输入密码:') sql = "select * from users where username = '%s' and password = '%s'" % (username, password) print(sql) # 执行SQL语句 cursor.execute(sql) results = cursor.fetchone() if results: print('登录成功') else: print('登录失败') # 关闭数据库连接 db.close()import pymysql def login(): # 打开数据库连接 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='12345', db='test', charset='utf8') # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() username = input('请输入用户名:') password = input('请输入密码:') sql = "select * from users where username = '%s' and password = '%s'" % (username, password) print(sql) # 执行SQL语句 cursor.execute(sql) results = cursor.fetchone() if results: print('登录成功') else: print('登录失败') # 关闭数据库连接 db.close()
2.1.正常登录
>>> login()请输入用户名:>? 张三请输入密码:>? 456123select * from users where username = '张三' and password = '456123'登录成功, 你好:张三>>> login() 请输入用户名:>? 张三 请输入密码:>? 456123 select * from users where username = '张三' and password = '456123' 登录成功, 你好:张三>>> login() 请输入用户名:>? 张三 请输入密码:>? 456123 select * from users where username = '张三' and password = '456123' 登录成功, 你好:张三
2.2.登录失败
>>> login()请输入用户名:>? 张三请输入密码:>? 123456select * from users where username = '张三' and password = '123456'用户名或密码错误,请重新输入>>> login() 请输入用户名:>? 张三 请输入密码:>? 123456 select * from users where username = '张三' and password = '123456' 用户名或密码错误,请重新输入>>> login() 请输入用户名:>? 张三 请输入密码:>? 123456 select * from users where username = '张三' and password = '123456' 用户名或密码错误,请重新输入
2.3.模拟注入
or '1' = '1'
的条件,此时不管密码是否正确都可以成功登录login()请输入用户名:>? 张三请输入密码:>? 123456' or '1' = '1select * from users where username = '张三' and password = '123456' or'1' = '1'登录成功, 你好:张三login() 请输入用户名:>? 张三 请输入密码:>? 123456' or '1' = '1 select * from users where username = '张三' and password = '123456' or '1' = '1' 登录成功, 你好:张三login() 请输入用户名:>? 张三 请输入密码:>? 123456' or '1' = '1 select * from users where username = '张三' and password = '123456' or '1' = '1' 登录成功, 你好:张三
3.解决方法,采用参数化查询
import pymysqldef login():# 打开数据库连接db = pymysql.connect(host='127.0.0.1', port=3306, user='root',passwd='12345', db='test', charset='utf8')# 使用 cursor() 方法创建一个游标对象 cursorcursor = db.cursor()username = input('请输入用户名:')password = input('请输入密码:')sql = "select * from users where username = %s and password = %s"# 执行SQL语句cursor.execute(sql, (username, password))results = cursor.fetchone()if results:print('登录成功, 你好:', username)else:print('用户名或密码错误,请重新输入')# 关闭数据库连接db.close()import pymysql def login(): # 打开数据库连接 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='12345', db='test', charset='utf8') # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() username = input('请输入用户名:') password = input('请输入密码:') sql = "select * from users where username = %s and password = %s" # 执行SQL语句 cursor.execute(sql, (username, password)) results = cursor.fetchone() if results: print('登录成功, 你好:', username) else: print('用户名或密码错误,请重新输入') # 关闭数据库连接 db.close()import pymysql def login(): # 打开数据库连接 db = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='12345', db='test', charset='utf8') # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() username = input('请输入用户名:') password = input('请输入密码:') sql = "select * from users where username = %s and password = %s" # 执行SQL语句 cursor.execute(sql, (username, password)) results = cursor.fetchone() if results: print('登录成功, 你好:', username) else: print('用户名或密码错误,请重新输入') # 关闭数据库连接 db.close()
3.1.正常登录
>>> login()请输入用户名:>? 张三请输入密码:>? 456123登录成功, 你好:张三>>> login() 请输入用户名:>? 张三 请输入密码:>? 456123 登录成功, 你好:张三>>> login() 请输入用户名:>? 张三 请输入密码:>? 456123 登录成功, 你好:张三
3.2.登录失败
>>> login()请输入用户名:>? 张三请输入密码:>? 123456用户名或密码错误,请重新输入>>> login() 请输入用户名:>? 张三 请输入密码:>? 123456 用户名或密码错误,请重新输入>>> login() 请输入用户名:>? 张三 请输入密码:>? 123456 用户名或密码错误,请重新输入
3.3.继续模拟注入
此处我们给SQL注入了一个 or '1' = '1'
的条件,此时我们使用的是参数化查询方式有效的防止了SQL注入
login()请输入用户名:>? 张三请输入密码:>? 123456' or '1' = '1用户名或密码错误,请重新输入login() 请输入用户名:>? 张三 请输入密码:>? 123456' or '1' = '1 用户名或密码错误,请重新输入login() 请输入用户名:>? 张三 请输入密码:>? 123456' or '1' = '1 用户名或密码错误,请重新输入
本网站的文章部分内容可能来源于网络,仅供大家学习与参考。
如有侵权请发送邮件到a-heartbeat-away@qq.com进行删除处理。
1 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
2 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
3 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
请登录后查看评论内容