Web安全 - SQL注入 ¶
作者:KK
发表日期:2017.8.24
快速认识SQL注入 ¶
假设以下PHP代码是为了返回一个用户列表,代码,看看SQL语句会长什么样:
$_GET['id'] = '1 or 1 = 1';
$sql = 'SELECT * FROM user WHERE id = ' . $_GET['id'];
mysql_query($sql); //....
以上的SQL语句拼接变量后是这样的:SELECT * FROM user WHERE id = 1 or 1 = 1
,其中注意条件是or逻辑,or后面的逻辑是成立的,造成不是age = 1的用户也能被查询出来
所以恶意攻击者只要继续修改id
参数就能导致程序查出更多数据了,其中上面是查询user表的所有字段,于是如果用or 1 = 1
这个条件查出所有人的话,那意味着攻击者有可能会获取所有用户的资料,甚至看到了密码和身份证号……
解决方案1:强制转换参数类型 ¶
代码改成这样:
$_GET['id'] = '1 or 1 = 1';
$id = (int)$_GET['id'];
$sql = 'SELECT * FROM user WHERE id = ' . $id;
mysql_query($sql); //....
不过如果条件参数本身就是一个字符串(比如email、name这样的字段)就麻烦了,用下面的方案2吧亲
解决方案2:参数化查询 ¶
以PHP语言为例,用PDO进行参数化查询
$pdo = new PDO("mysql:host=localhost;dbname=database","dbusername", "dbpassword");
$username= "'aaa' or 1 = 1";
$password= "someword";
$query = "SELECT * FROM users WHERE (name = :username) and (password = :password)";
$statement = $pdo->prepare($query,array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$statement->bindParam(":username", $username, PDO::PARAM_STR, 10); //绑定参数
$statement->bindParam(":password", $password, PDO::PARAM_STR, 12); //绑定参数
$statement->execute();
结果SQL会被PDO转换成这样:SELECT * FROM users WHERE (name = '\'aaa\' or 1 = 1') and (password = 'someword')
,这样整个'aaa' or 1 = 1
就变成了一个字符串,而不能形成对SQL语法的干扰控制
如果你还在自己手动拼接SQL就是低水平的程序员,除非你的应用根本不关心这些安全问题
尽量用数组作为条件,方便底层自动进行参数绑定 ¶
function where($condition){
$sql = 'SELECT * FROM user ';
if(is_string($condition)){
return $sql . $condition;
}
if(is_array($condition)){
//数组条件,进行参数绑定,实际上的框架往往不只是这么简单,反正能帮你做绑定
foreach($condition as $field => $value){
$sql .= '(' . $field . ' = :' . $field . ')';
}
$statement = $pdo->prepare($sql);
foreach($condition as $field => $value){
$sql .= '(' . $field . ' = :' . $field . ')';
$statement->bindParam(':' . $field, $value)
}
return $statement;
}
}
$_GET['name'] = 'a" or "1" = "1';
echo where('name = "' . $_GET['name'] . '"');
// SELECT * FROM user name = "a" or "1" = "1"
echo where(['name' => $_GET['name']]);
//PDOStatement对象,最终执行语句是 SELECT * FROM user (name = '"a" or "1" = "1"')