如果用户的输入不加修改就插入到SQL查询里,这个应用程序会容易受到SQL注入,就像下面这样:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
这是因为用户能够输入像value'); DROP TABLE table;--之类的代码,然后这个查询就变成了:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
怎么才能防止这样的情况发生呢?
支持率最高回答:
使用预处理语句很参数化查询。这些是被数据库服务器独立于任何参数进行发送和解析的SQL语句。这种方法让攻击者注入恶意代码变得不可能。
你基本上有两种选择来实现:
1.使用PDO:
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(array('name' => $name));
foreach ($stmt as $row) {
// do something with $row
}
2.使用MySQLi:
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// do something with $row
}
PDO
需要注意的时,当使用PDO访问MySQL数据库时,默认情况下不使用真正的预处理语句。要解决这个问题你必须金庸预处理语句的仿真。使用PDO创建链接的一个示例如下:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user',
'pass');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
上面的示例里的错误模式不是严格必须的,但是建议添加上它。这样的脚本在出现错误时不会因为致命错误而终止,并且他还让开发者有机会捕捉到PDO异常抛出的任何错误。
第一行的setAttribute( )是强制性的,它告诉PDO禁用模拟预处理语句并使用真正的预处理语句。这保证了语句和值在被发送到MySQL服务器之前不会被PHP解析(不给潜在的攻击者注入恶意的SQL的机会)。
虽然你可以在构造函数的选项中设置字符集,但要注意旧版本的PHP(小于5.3.6)会在DSN中自动忽略字符集参数。
解释
上面的代码会让你传给prepare的SQL语句被数据库服务器解析并编译。通过指定参数(可以是一个问号或者上面例子里的像:name的命名参数)你告诉数据库引擎过滤哪些部分。然后你调用执行,预处理语句和你指定的参数值就结合起来了。
这里重要的是,参数值是与已经编译的语句进行结合,而不是与SQL字符串。SQL注入通过在它创建SQL发送到数据库时用包含恶意的字符串欺骗脚本的方式来实现。所以通过发送独立于参数的实际的SQL,你就会限制住一些你并不希望出现的事情的风险。你使用预处理语句发送的任何参数都只会被视为字符串(尽管数据库引擎可能会做一些优化,然后参数也可能成为数字)。在上面的示例中,如果$name变量包含'Sarah';DELETE FROM employees结果仅仅是对字符串"'Sarah';DELETE FROM employees"的搜索,你不会得到一个空表。
使用预处理语句的另一个好处就是如果你在同一个会话中多次执行相同的语句只会被解析和编译一次,这会给你一些速度上的益处。既然你问到了如何插入,这里有一个使用PDO的示例:
$preparedStatement = $db->prepare('INSERT INTO table (column)
VALUES (:column)');
$preparedStatement->execute(array('column' => $unsafeValue));
原文:http://www.php100.com/html/it/focus/2014/1020/7539.html