主要知识点
数据库字符串比较:在数据库对字符串进行比较时,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,令两个字符串的长度一致,比如,字符串A:[String]和字符串B:[String2]进行比较时,由于String2比String多了一个字符串,这时MySQL会将字符串A填充为[String ],即在原来字符串后面加了一个空格,令两个字符串长度一致。看如下两条查询语句:
select * from users where username=‘Dumb’
select * from users where username='Dumb ’
它们的查询结果是一致的,即第二条查询语句里Dumb后面的空格并没有对查询有任何影响。因为在MySQL把查询语句里的username和数据库里的username值进行比较时,它们就是一个字符串的比较操作,符合上述特征。
INSERT截断:这是数据库的另一个特性,当设计一个字段时,我们都需对其设定一个最大长度,比如CHAR(10),VARCHAR(20)等等。但是当实际插入数据的长度超过限制时,数据库就会将其进行截断,只保留限定的长度。
SQL约束攻击
后台在接收到用户名与密码后使用了php的函数mysql_real_escape_string()处理,并使用单引号包裹。验证用户名与密码的代码如下:
按理说上述代码不会产生SQL注入等漏洞的啊,下面介绍几类关键知识:
在SQL里执行字符串处理时,字符串末尾的空格符将会被删除。例如如下代码:
上述代码和username = 'test’结果是一样的。但也存在异常情况,最好的例子就是LIKE子句了。注意,对尾部空白符的这类修剪操作,主要是在“字符串比较”期间进行的。这是因为,SQL会在内部使用空格来填充字符串,以便在比较之前令其它们的长度保持一致。
在所有的INSERT查询里,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于“n”个字符,那么仅插入字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“testName”时,实际上只能插入字符串的前5个字符,即“testN”。
当注册时,后台一般先select一下用户名看看是否存在,然后在insert一下。示例代码如下:
这里注册时使用用户名+【大量空格】和随机密码注册即可完成攻击。
主要原理就是insert时候有varchar(n)的限制,大于n的时候会截取前n个存入。在数据库对字符串进行比较时,即select操作,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,令两个字符串的长度一致。注册时select语句不会将"admin+[大量空格]11"删减到n位,所以不会被select查出与admin重复,不会返回数据,接下来就可以插入admin+[空格](截取)11和自定义密码了。
如果使用用户名“vampire”和密码“random_pass”登录的话,对比时是admin与admin+[大量空格],会将前面的admin添加空格与后面的长度相同在进行对比,那么返回的只能是我们自己注册的用户信息,而不会返回目标用户信息。SQL查询语句是一个and操作,如果密码不一样怎么会把目标用户的信息也返回回来?
当登陆时使用admin与自定义密码登陆,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。