好问题
Good  Question
  • 首 页
  • 问题
    • PHP
    • JAVA
    • CPlusPlus
    • C#
    • SQL
  • 关 于
  • 联 系
如何在PHP中防止SQL注入? 关闭 返回上一级  

如何在PHP中防止SQL注入?
+ 查看更多

发布日期:2018-03-10 14:53
分类:PHP
浏览次数:86

如果用户输入未经修改地插入到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(对于MySQL来说):
$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
}
如果你连接到除了mysql以外的数据库,有个特定的驱动程序第二个选项你可参考(例如PostgreSQL是*pg_prepare()*和*pg_execute()*)。PDO是通用的选项。
正确设置连接
注意,在默认情况下使用PDO访问mySQL数据库时,实际上不会执行预处理语句。为了解决这个问题,你应该禁止PDO模拟预处理语句。使用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);
在上述示例中,错误模式不是严格必需的,但建议将其添加。这样,当出现问题是发生 Fatal Error 时,脚本不会停止运行,并且给了程序员 catch 作为 PDOExceptions 实例被 thrown 的机会。
然而,第一个 setAttribute() 调用是强制的,它禁止PDO模拟预处理语句,而使用真正的预处理语句。这样能确保语句和参数值在发送给MySQL服务器之前没有被PHP解析处理过(这将使得攻击者无法注入恶意SQL)。虽然你可以在构造函数的选项中设置 charset ,但重要的是要注意在老版本的PHP(<5.3.6)中,你在DSN上设置的字符集参数是会被自动忽略的。

解释

当你发送sql语句时,数据库服务器会进行执行前预处理的解析和编译。通过指定参数(一个问号或者在上面例子中命名的 :name ),告诉数据库引擎需要过滤哪些参数。然后当你调用
execute
时,预处理语句会和你指定的参数值相结合。需要值得强调的是,*参数值是和经过编译的语句相结合,而不适合sql字符串* 。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));
预处理语句可以用于动态查询吗?
虽然你仍然可以用预处理语句进行查询参数,但是动态查询它本身的结构不能进行参数化,并且查询的特点是不能被参数化。
对于这些特殊情况,最好的方法是使用可以限制可能值的白名单过滤器。

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
除了使用预处理函数,你也可以通过PHP函数来 消毒 用户输入的代码,例如下面的函数:
function inject_check($sql_str) {
    return eregi('select|insert|and|or|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $sql_str);
}

function verify_id($id=null) {
    if(!$id) {
        exit('没有提交参数!');
    } elseif(inject_check($id)) {
        exit('提交的参数非法!');
    } elseif(!is_numeric($id)) {
        exit('提交的参数非法!');
    }
    $id = intval($id);

    return $id;
}

function str_check( $str ) {
    if(!get_magic_quotes_gpc()) {
        $str = addslashes($str); // 进行过滤
    }
    $str = str_replace("_", "\_", $str);
    $str = str_replace("%", "\%", $str);

   return $str;
}

function post_check($post) {
    if(!get_magic_quotes_gpc()) {
        $post = addslashes($post);
    }
    $post = str_replace("_", "\_", $post);
    $post = str_replace("%", "\%", $post);
    $post = nl2br($post);
    $post = htmlspecialchars($post);

    return $post;
}
上一篇如何在PHP中解析和处理HTML / XML?
PHPMailer :SMTP Error: Could not connect to SMTP host.下一篇
下一篇PHPMailer :SMTP Error: Could not connect to SMTP host.

最新文章

  • 函数`__construct`用来干嘛的
    发布日期:2018-03-26
  • 通过访客的IP得到他们的地区
    发布日期:2018-03-26
  • 合并两个PHP对象的最好的方法是什么?
    发布日期:2018-03-26
  • 该如何把一该如何把一个对象转化成数组?
    发布日期:2018-03-26
  • 什么是输出缓冲区?
    发布日期:2018-03-26
  • 在PHP中怎么把用逗号分隔的字符串分隔在一个数组里?
    发布日期:2018-03-26
  • 在PHP中使用foreach循环时查找数组的最后一个元素
    发布日期:2018-03-26
关于好问
收集整理一些有用的问题和回答,造福中国的程序旺和IT喵们!
友情链接
起飞页 
相关信息
版权声明
Copyright © 2016 - 2022  苏州卡达网络科技有限公司 备案号:苏ICP备09008221号