前言
  值得庆幸的是如今开发者在构建网站时,已经开始注重安全问题了。绝大部分开发者都意识到SQL注入漏洞的存在,在本文我想与读者共同去探讨另一种与SQL数据库相关的漏洞,其危害与SQL注入不相上下,但却不太常见。接下来,我将为读者详细展示这种攻击手法,以及相应的防御策略。
  注意:本文不是讲述SQL注入攻击
  背景介绍
  近,我遇到了一个有趣的代码片段,开发者尝试各种方法来确保数据库的安全访问。当新用户尝试注册时,将运行以下代码:
<?php
// Checking whether a user with the same username exists
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT *
FROM users
WHERE username='$username'";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0) {
// User exists, exit gracefully
.
.
}
else {
// If not, only then insert a new entry
$query = "INSERT INTO users(username, password)
VALUES ('$username','$password')";
.
.
}
}
  使用以下代码验证登录信息:
<?php
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT username FROM users
WHERE username='$username'
AND password='$password' ";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0){
$row = mysql_fetch_assoc($res);
return $row['username'];
}
}
return Null;
  安全考虑:
  · 过滤用户输入参数了吗? — 完成检查
  · 使用单引号(’)来增加安全性了吗? — 完成检查
  按理说应该不会出错了啊?
  然而,攻击者依然能够以任意用户身份进行登录!
  攻击手法
  在谈论这种攻击手法之前,首先我们需要了解几个关键知识点。
  1、在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说“vampire”等同于“vampire ”,对于绝大多数情况来说都是成立的(诸如WHERE子句中的字符串或INSERT语句中的字符串)例如以下语句的查询结果,与使用用户名“vampire”进行查询时的结果是一样的。
  SELECT * FROM users WHERE username='vampire     ';
  但也存在异常情况,好的例子是LIKE子句了。注意,对尾部空白符的这种修剪操作,主要是在“字符串比较”期间进行的。这是因为,SQL会在 内部 使用空格来填充字符串,以便在比较之前使其它们的长度保持一致。
  2、在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的大长度。也是说,如果字符串的长度大于“n”个字符的话,那么仅使用字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“vampire”时,实际上只能插入字符串的前5个字符,即“vampi”。
  现在,让我们建立一个测试数据库来演示具体攻击过程。
  vampire@linux:~$ mysql -u root -p
  mysql> CREATE DATABASE testing;
  Query OK, 1 row affected (0.03 sec)
  mysql> USE testing;
  Database changed
  接着创建一个数据表users,其包含username和password列,并且字段的大长度限制为25个字符。然后,我将向username字段插入“vampire”,向password字段插入“my_password”。
mysql> CREATE TABLE users (
->   username varchar(25),
->   password varchar(25)
-> );
Query OK, 0 rows affected (0.09 sec)
mysql> INSERT INTO users
-> VALUES('vampire', 'my_password');
Query OK, 1 row affected (0.11 sec)
mysql> SELECT * FROM users;
+----------+-------------+
| username | password    |
+----------+-------------+
| vampire  | my_password |
+----------+-------------+
1 row in set (0.00 sec)