一、sql注入原理
SQL注入就是把SQL命令插入到Web表单然后提交到所在页面请求(查询字符串),从而达到欺骗服务器执行恶意的SQL命令。
它是利用现在已有的应用程序,将SQL语句插入到数据库中执行,执行一些并非按照设计者意图的SQL语句。
产生原因:
是程序没有细致过滤用户输入的数据,从而导致非法数据进入系统。
相关技术原理:
SQL注入可以分为
平台层注入和
代码注入。前者是由不安全的数据库配置或平台漏洞所致。后者是程序员对输入未进行细致过滤产生。
这种攻击的要诀在于将SQL的查询/行为命令通过‘嵌入’的方式放入合法的HTTP提交请求中从而达到攻击者的某种意图。现在很多的动态网页都会从该网页使用者的请求中得到某些参数,然后动态的构成SQL请求发给数据库的。
举个例子,当有某个用户需要通过网页上的用户登陆(用户身份验证)时,动态网页会将该用户提交上来的用户名与密码加进SQL询问请求发给数据库,用于确认该用户提交的身份验证信息是否有效。在SQL注入攻击的角度看来,这样可以使我们在发送SQL请求时通过修改用户名与/或密码值的‘领域’区来达到攻击的目的。
注入的手段无非是在拼接字符串的时候加入一些SQL关键字。要防止SQL注入,就要对传入的字符串进行检查,对包含SQL关键字的输入都视为非法
二、SQL注入点判断
传入SQL语句可控参数分为两类:
1. 数字类型,参数不用被引号括起来,如
?id=1
2. 字符类型,参数要被引号扩起来,如
?name="phone"
判断某个链接是否存在SQL注入,可以通过对其传入的可控参数进行简单的构造,通过服务端返回的内容来判断有无注入。
字符类型:
构造测试 预期结果 变种
a'
//触发错误,返回数据库错误
a
' or '
1
'='
1
//永真条件,返回所有记录 a
') or ('
1'=1
a
' or '
1
'='
2
//空条件,返回原来相同结果 a
') or ('
1'=2
a
' and '
1
'='
2
//永假条件,不返回记录 a
') and ('
1
'='
2
数字类型:
构造测试 预期结果 变种
' //触发错误,返回数据库错误
1
+
1
//返回原来相同的结果
3
-
1
1
+
0
//返回原来相同的结果
1
or
1
=
1
//永真条件,返回所有记录
1
)
or
(
1
=
1
1
or
1
=
2
//空条件,返回原来相同的结果
1
)
or
(
1
=
2
1
and
1
=
2
//永假条件,不返回记录
1
)
and
(
1
=
2
三、危害
SQL注入的
危害不仅体现在数据库层面上,还有可能危及承载数据库的操作系统
;如果SQL注入被用来挂马,还可能用来传播恶意软件等,这些危害包括但不局限于:
1.数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。作为数据的存储中心,数据库里往往保存着各类的隐私信息,SQL注入攻击能导致这些隐私信息透明于攻击者。
2.网页篡改:通过操作数据库对特定网页进行篡改。
3.网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。
4.数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
5.服务器被远程控制,被安装后门。经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。
6.破坏硬盘数据,瘫痪全系统。
一些类型的数据库系统能够让SQL指令操作文件系统,这使得SQL注入的危害被进一步放大。
四、解决方案
解决SQL注入问题的关键是对所有可能来自用户输入的数据进行严格的检查、对数据库配置使用最小权限原则。
经常使用的方案有:
1.所有的查询语句都使用数据库提供的
参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中。当前几乎所有的数据库系统都提供了参数化SQL语句执行接口,使用此接口可以非常有效的防止SQL注入攻击。
2.
对进入数据库的特殊字符('"\<>&*;等)进行转义处理,或编码转换。
3.要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等
3.确认
每种数据的类型,比如数字型的数据就必须是数字,数据库中的存储字段必须对应为int型。
4.数据长度应该严格规定,能在一定程度上防止比较长的SQL注入语句无法正确执行。
5.网站每个数据层的
编码统一,建议全部使用UTF-8编码,上下层编码不一致有可能导致一些过滤模型被绕过。
6.严格限制网站用户的数据库的操作权限,给此用户提供仅仅能够满足其工作的权限,从而最大限度的减少注入攻击对数据库的危害。
7.
避免网站显示SQL错误信息,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断。
8.在网站发布之前建议使用一些专业的
SQL注入检测工具进行检测,及时修补这些SQL注入漏洞。
9.限制 Web 应用程序所用的数据库访问帐号权限。一般来说,应用程序没有必要以 dbo 或者 sa 的身份访问数据库。需要严格遵循权限最小化原则,根据业务需要建立数据库登录用户,设置足够健壮的密码,只分配必要的权限及可访问数据库。
4.1、参数化防止SQL注入
sql注入中最常见的就是字符串拼接,研发人员对字符串拼接应该引起重视错误用法1:sql = "select id, name from test where id=%d and name='%s'" % (id, name)cursor.execute(sql)错误用法2:sql = "select id, name from test where id="+ str(id) +" and name='"+ name +"'"cursor.execute(sql)正确用法1:(Mysql数据库)sql = "select id, name from test where id=%s and name=%s"cursor.execute(sql, (id, name) ) # python会自动过滤args中的特殊字符,制止SQL注入的产生execute()函数本身有接受sql语句参数位的,可以通过python自身的函数处理sql注入问题。如果只有一个参数,则写成:cursor.execute(sql,(id,))正确用法2:name = MySQLdb.escape_string(name)sql = "select id, name from test where id=%s and name=%s" %(id, name)cursor.execute(sql)python模块MySQLdb自带针对mysql的字符转义函数escape_string,可以对字符串转义。
防止like的SQL注入:
Mysql数据库:
sql = " and indexNum like concat('%',?,'%') "
Oracle:
sql = " like '%' || ? || '%' "
SQL Server:
sql = " like '%' + ? + '%' "
WHERE Name LIKE CONCAT('%',@name,'%')
sqlite3:
rulename = rule_name + "%"
sql = ''' UPDATE {} SET has_alert=1 WHERE device_id=? AND rule LIKE ?;'''
cursor.execute(sql.format(table),(did, rulename))
模糊查询防sql注入
:
def sub_type_total(self, taskid, type, subtype, aggregate_name, keyword):
if keyword:
sql = 'SELECT count(1) FROM tmp_tbl where type=? and subtype=? and aggregate_name=? and rawlog like ?;'
else:
sql = 'SELECT count(1) FROM tmp_tbl where type=? and subtype=? and aggregate_name=?;'
with sqlite3.connect(self.getDB(taskid)) as conn:
cursor = conn.cursor()
cursor.execute('PRAGMA case_sensitive_like = false;') # 忽略内置LIKE 操作符字母的大小写
if keyword:
cursor.execute(sql, (type, subtype, aggregate_name, "%" + keyword + "%"))
else:
cursor.execute(sql, (type, subtype, aggregate_name))
return cursor.fetchone()[0]
4.2、过滤特殊字符
过滤非法字符:在开发过程中,要对前端传过来的数据进行验证,防止sql注入攻击,其中的一个方案就是过滤用户传过来的非法的字符
Python sql注入 过滤字符串的非法字符 - 淋哥 - 博客园 (cnblogs.com)
def sql_filter(sql, max_length=20):
dirty_stuff = ["\"", "\\", "/", "*", "'", "=", "-", "#", ";", "<", ">", "+", "%", "$", "(", ")", "%", "@","!", "|",]
for stuff in dirty_stuff:
sql = sql.replace(stuff, "")
return sql[:max_length]
username = "1234567890!@#!@#!@#$%======$%"
username = sql_filter(username) # SQL注入
print(username)
# 输出结果是:1234567890
五、参考
(python)PyMySQL模块的使用与SQL注入_miaoqinian的博客-CSDN博客
06 SQL 注入:小心数据库被拖走(上)_哔哩哔哩_bilibili