SQL注入是将Web页面的原URL、表单域或数据包输入的参数,修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器以执行数据库命令。
比如,网页一般需要根据用户名和密码去查询数据库,如果数据库中含有相应记录,则返回登录成功,否则返回登录失败。在该场景中,web服务器需要根据用户输入的信息动态查询数据库。即用户输入的信息,将直接放到服务器查询数据库的SQL语句中。这意味着后台的操作,会受用户输入影响。如果后端没有对输入信息进行必要的检查,便可能发生数据库信息泄露的问题。
01、SQL注入举例
1. 猜解和篡改后台数据库
举例:某网站可以根据用户输入的id来查询对应的用户名,即对输入的user_id,执行下面的SQL语句:
select
name
from
users
where
id
=
'user_id'
xxx(其他的一些逻辑)
这便存在注入风险。比如,用户可以填写user_id为
1' order by 1 #
,这样后端的SQL就变为:
select
name
from
users
where
id
=
'1'
order
by
1
#xxx (#会将后面的逻辑给注释掉
这样,便会查找id = 1 且根据表中的第一个字段来进行排序
如果返回成功,那说明表中存在第一个字段,同理可以用 order by 2,3,4...依次去试,例如试到4时发现网页报错,说明数据库存储用户信息的表只有3个字段。在此基础上,可以查询到更多的数据库信息。
查询也可以用来返回所有表信息,如输入
1' or 1=1 #
select
name
from
users
where
id
=
'1'
or
1
=
1
#' xxx(其他的一些逻辑)
由于or后面的语句恒为真,因此where语句失效,数据库返回整个表信息
如果通过输入
1' and 1;delete from users #
拼接出如下的SQL语句,那么会删除整个表
select
name
from
users
where
id
=
'1'
or
1
=
1
;
delete
from
users
#' xxx
2. 欺骗验证
举例:在登录时,一般要求验证用户名和密码,对应后台的验证逻辑是:
select
*
from
users
where
name
=
'name1'
and
passward =
'ps1'
正常情况下,当查询数据表得到满足条件的记录时,会返回登录成功
很显然,我们可以修改输入的用户名和密码,如用户名框输入
a' or 1=1#
,密码框输入
xx
,则后台拼接成的SQL语句为:
select
*
from
users
where
name
=
'a'
or
1
=
1
#' and passward = 'xx'
由于#号的注释,上面语句等效为:
select
*
from
users
where
name
=
'a'
or
1
=
1
该语句恒为真,直接就绕过了登录验证
02、判断SQL注入点和注入类型
一般比较明显的SQL注入漏洞的URL有:http://xxx.xxx.xxx/abcd.php?id=1,这种从字面就能读出来后端是根据输入id的值来做判断。一般而言,所有有用户输入的动态页面+有查询数据库的操作,都有注入风险。
常见的测试方法是单引号测试法,如输入id的值为 1',那么对应的SQL语句为
// id类型为数字
select * from users where id = 1'
// id类型为字符串
select * from users where id = '1''
如果网页报错,说明存在注入风险,因为无论后端采用的是数字类型还是字符串类型,都会因为单引号不匹配而报错
判断注入类型,例如页面输入的信息可能被当作数字型,也可能字符串类型,那么可以用and条件来测试:
第一次输入id:1 and 1=1
第二次输入id:1 and 1=2
如果后端采取的是数字型,则SQL语句为:
select * from users where id = 1 and 1=1
select * from users where id = 1 and 1=2
第一次成功,第二次失败
如果后端采取的是数字型,则SQL语句为:
select * from users where id = '1 and 1=1'
select * from users where id = '1 and 1=2'
第一次和第二次都会返回失败
类似的,也可以用该原理来测试是否为字符串类型的注入
03、SQL注入的预防
从上面的例子可以看到预防SQL注入的必要性,一般的防范措施如下:
1. 过滤和强校验
对输入的参数进行强限制,如数字类型的必须全为数字,字符串类型的必须符合规定的正则表达式。去掉字符串中容易造成注入的分号和单引号(Trim)
2. 使用存储过程
使用更安全的数据库操作接口,而非直接执行一条拼接的SQL语句
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升
3. 对用户密码进行强加密
即使用户密码被泄露,黑客拿到的也是加密后的密码,无法反解出原密码