ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

PHP中如何防止SQL注入

2020-06-05 13:04:01  阅读:286  来源: 互联网

标签:语句 statements name prepared PDO SQL PHP 注入


这是StackOverFlow上一个投票非常多的提问 How to prevent SQL injection in PHP?   我把问题和赞同最多的答题翻译了下来。

 

提问:如果用户的输入能直接插入到SQL语句中,那么这个应用就易收到SQL注入的攻击,举个例子:

 

$unsafe_variable = $_POST['user_input'];
 
mysqli_query("INSERT INTO table (column) VALUES ('" . $unsafe_variable . "')");

用户可以输入诸如 :  value'); DROP TABLE table;--   ,SQL语句就变成这样了:

 

INSERT INTO table (column) VALUES('value'); DROP TABLE table;--')

(译者注:这样做的结果就是把table表给删掉了) 我们可以做什么去阻止这种情况呢?

 


回答:

使用prepared statements(预处理语句)和参数化的查询。这些SQL语句被发送到数据库服务器,它的参数全都会被单独解析。使用这种方式,攻击者想注入恶意的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数据库时,真正的prepared statements默认情况下是不使用的。为了解决这个问题,你需要禁用模拟的prepared statements。下面是使用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 Exceptions,这就给了开发者机会去捕获这个错误。

 

然而第一行的 setAttribute() 是强制性的,它使得PDO禁用模拟的prepared statements并使用真正的prepared statements。这可以确保这些语句和值在被发送到MySQL服务器之前不会被PHP解析(这使得攻击者没有注入恶意SQL的机会)。

 

尽管你可以使用可选的构造函数参数去设置 charset ,但重点需要注意的是小于5.3.6的PHP版本,DSN(Data Source Name)是默认忽略 charset 参数的。

 

说明

当你传一个SQL语句做预处理时会发生什么?它被数据库服务器解析和编译了。通过指定参数(通过之前例子中的 ? 或者像 :name 这样的命名式参数)你告诉数据库引擎你是想过滤它。接着当你调用 execute() 函数时,prepared statements会和你刚才指定的参数的值结合。

 

在此重要的是,参数的值是和编译过的语句结合,而非一个SQL字符串。SQL注入就是当创建被发送到数据库的SQL语句时,通过欺骗的手段让脚本去引入恶意的字符串。因此当你使用单独的参数发送真实正确的SQL时,你就限制了被某些不是你真实意图的事情而搞挂掉的风险。使用prepared statements 传递的任何参数都会被当做字符串对待(不过数据库引擎可能会做一些优化,这些参数最终也可能变成numbers)(译者注:意思就是把参数当做一个字符串而不会去做额外的行为)。比如在上面的例子中,如果 $name 变量的值是 'Sarah'; DELETE * FROM employees  ,产生的结果是会去搜索"'Sarah'; DELETE * FROM employees"这一整个字符串,最终的结果你也就不会面对的是一张空表了。

 

使用prepared statements的另一个好处是,如果你在同一session中再次执行相同的语句,也就不会被再次解析和编译,这样你就获得一些速度上的提升。

 

哦,既然你问的是插入操作该如果防止,下面给出一个例子(使用PDO):

 

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
 
$preparedStatement->execute(array(':column' => $unsafeValue));

 

 

翻译完,本人翻译水平有限,请读者见谅。

标签:语句,statements,name,prepared,PDO,SQL,PHP,注入
来源: https://www.cnblogs.com/ksy-c/p/13049126.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有