假设你用自己的用户名和密码登录了一个付费网站,网站服务器就会查询一下你是不是 VIP 用户,而用户数据都是放在数据库中的,服务器通常都会向数据库进行查询,要向数据库进行增删改查操作,就需要用到 SQL 语言。
但是,作为 SQL 的注入攻击者,我们并不知道网站的密码是什么,甚至都不知道用户名是什么,那就不能按正常套路出牌了。
这个时候我们不输入正常的数据,而是把数据转换成代码,使得服务器向数据库的正常查询变成了不正常的代码执行, 那么攻击者就可以执行自己想要的操作了。
如下代码,这是正常的查询操作,只有用户名和密码都匹配时,才能成功登陆。
select * from users
where
username='栈老师' and password='123456'
但是服务器如果没有过滤用户输入的数据,就很危险了!比如说,我们在用户名最后加上一个单引号,在 SQL 执行的时候,一对引号里面的东西会视为字符数据,但是因为这里的引号不成对,因此这条查询语句是会直接报错的,如果此时服务器把这条 SQL 报错信息直接返回给用户,那么攻击者就知道具体是什么原因导致报错了, SQL 注入攻击的门也就这样打开了。
如果我们在用户名的单引号后面加上两个-,事情就不一样了。在 mysql 内部,系统遇到两条横杠就会认为从此处开始到该行结束,中间的内容都是注释,而注释是不会被视为代码执行的, 相当于只会执行前面部分的 SQL 语句。
所以只知道用户名,即使没有密码,也是可以进行登录的,代码如下:
select * from users
where
username='栈老师' --' and password='123456'
上面是只知道用户名不知道密码的情况。那如果我们连正确的用户名信息都没有呢?
SQL 语句在遇到 where 关键词的时候会判断条件是真还是假,我们把这里的条件放在 or 里面进行拼接,因为 or 的一边为真,那么整体就为真,所以我们只需要再增加一个为真的条件就可以了, 比如 1=1,这个时候即使用户信息错误,where 的逻辑判断也总是为真。
那么即使不知道用户名和密码,依然可以登录:
select * from users
where
username='栈老师' or 1=1
我们都知道合并两个表格可以用 union 关键字,像下面这样:
select * from table1
union
select * from table2
mysql 规定,union select 的后面可以不指定表名,只需要两个表格的列数相同、数据类型对应即可。
所以我们直接在 select 后面用 null 来代替列名。null 代表没有值,但是 union 必须要保证合并两边的列数是一致的,攻击者就可以通过增加 null 的个数来测出表格的列数。
select * from products
union
select null, null, null
攻击者现在用其他方法知道了另一个表格(用户表)的信息,那就可以把表格的列名放在 null 的位置,并且指定选取的表格名。
但是 union 除了要保证列数一致以外,还要保证数据类型的相似性,否则是无法进行合并的。
实际上攻击者在确定好列数以后,还会进行数据类型的判断,比如逐列进行测试。测试完毕之后就可以把所有的东西都放在一起了。
首先在可以注入的地方,用单引号强行终止数据内容,并且加上两个横杠使得后面的语句失效,这样 SQL 语句就不会报错。
这样,攻击者就可以在此处注入 union 语句了:
select * from products
where name = 'apple'
union
select null, username, password from users --'
此时,账号密码信息就这样被合并到 products 表中了!