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"')