PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决

这篇是上一篇 http://www.cnblogs.com/charlesblc/p/5987951.html 的续集。

看有的文章提到mysqli和PDO都支持多重查询,所以下面的url会造成表数据被删。

http://localhost:8080/test.php?id=3;delete%20from%20users

可是我在mysql版本的函数,上面的sql都不能执行。是不是不支持多重查询了?

 

这篇文章 http://www.runoob.com/php/php-mysql-connect.html 对mysqli, PDO的方式有一些介绍,不详细。

主要用的这篇文章:http://blog.csdn.net/yipiankongbai/article/details/17277477

三种连接方式:

// PDO  
$pdo = new PDO("mysql:host=localhost;dbname=database", 'username', 'password');  
   
// mysqli, procedural way  
$mysqli = mysqli_connect('localhost','username','password','database');  
   
// mysqli, object oriented way  
$mysqli = new mysqli('localhost','username','password','database');  

 

先看mysqli过程型:

复制代码
<?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$con = mysqli_connect('10.117.146.21:8306', 'root', '[password]');
mysqli_select_db($con, 'springdemo');

$input_id = trim($_GET['id']);
$sql = 'select nickname from user where id = ' . $input_id;
print_r('SQL is:' . $sql . '<br/>');
$result = mysqli_query($con, $sql);

if ($result != null) {
  print_r('rows:' . mysqli_num_rows($result) . '<br/>');
  while ($row = mysqli_fetch_array($result)) {
    print_r($row['nickname'] . '<br/>');
  }
}

mysqli_close($con);
?>
复制代码

测试:

复制代码
http://localhost:8080/test.php?id=3

PHP version:5.5.30
SQL is:select nickname from user where id = 3
rows:1
micro

http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
SQL is:select nickname from user where id = 3 or 1=1
rows:4
abc
micro
helloworld
你好
复制代码

加上real_escape函数:

复制代码
?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$con = mysqli_connect('10.117.146.21:8306', 'root', '[password]');
mysqli_select_db($con, 'springdemo');

$input_id = mysqli_real_escape_string($con, $_GET['id']);
$sql = 'select nickname from user where id = ' . $input_id;
print_r('SQL is:' . $sql . '<br/>');
$result = mysqli_query($con, $sql);

if ($result != null) {
  print_r('rows:' . mysqli_num_rows($result) . '<br/>');
  while ($row = mysqli_fetch_array($result)) {
    print_r($row['nickname'] . '<br/>');
  }
}

mysqli_close($con);
?>
复制代码

测试:

复制代码
http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
SQL is:select nickname from user where id = 3 or 1=1
rows:4
abc
micro
helloworld
你好

注:仍然有问题,因为没有在url里面加引号!
复制代码

采用推荐的mysqli的PreparedStatement方式:

复制代码
?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$con = new mysqli('10.117.146.21:8306', 'root', '[password]', 'springdemo');
//mysqli_select_db($con, 'springdemo');

$query = $con->prepare('SELECT nickname FROM user WHERE id = ?');
$query->bind_param('s', $_GET['id']);
$query->execute();

$result = $query->get_result();
if ($result != null) {
  print_r('rows:' . mysqli_num_rows($result) . '<br/>');
  while ($row = mysqli_fetch_array($result)) {
    print_r($row['nickname'] . '<br/>');
  }
}

mysqli_close($con);
?>
复制代码

测试:

复制代码
http://localhost:8080/test.php?id=3

PHP version:5.5.30
rows:1
micro

http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
rows:1
micro
复制代码

 

PDO与mysqli的对比:

性能
PDO和MySQLi都有非常好的性能。在非prepared statements的基准测试下,MySQLi略快2.5%,而prepared statements下是6.5%,可以说对于性能无关紧要。

 

  PDO MySQLi
Database support 12 different drivers MySQL only
API OOP OOP + procedural
Connection Easy Easy
Named parameters Yes No
Object mapping Yes Yes
Prepared statements 
(client side)
Yes No
Performance Fast Fast
Stored procedures Yes Yes

 PDO方式:

复制代码
<?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$pdo = new PDO("mysql:host=10.117.146.21:8306;dbname=springdemo", 'root', '[password]');

class Name {
   public $nickname;

   public function info()
   {
      return '#'.$this->nickname;
   }
}

$input_id = $_GET['id'];
$sql = 'select nickname from user where id = ' . $input_id;
print_r('SQL is:' . $sql . '<br/>');
$result = $pdo->query($sql);
$result->setFetchMode(PDO::FETCH_CLASS, 'Name');

if ($result != null) {
  while ($row = $result->fetch()) {
    print_r($row->info() . '<br/>');
  }
}

$pdo=null;
?>
复制代码

测试:

复制代码
http://localhost:8080/test.php?id=3

PHP version:5.5.30
SQL is:select nickname from user where id = 3
#micro

http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
SQL is:select nickname from user where id = 3 or 1=1
#abc
#micro
#helloworld
#你好
复制代码

加上转码:

$input_id = $pdo->quote($_GET['id']);

测试:

复制代码
http://localhost:8080/test.php?id=3

PHP version:5.5.30
SQL is:select nickname from user where id = '3'
#micro

http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
SQL is:select nickname from user where id = '3 or 1=1'
#micro
复制代码

注意,pdo的quote自动加了引号,解决了这个问题。

http://localhost:8080/test.php?id=3%27%20or%201=%271

PHP version:5.5.30
SQL is:select nickname from user where id = '3\' or 1=\'1'
#micro

并且,尝试自己加引号去做侵入也没有用的。引号被转码了,所以成功防住攻击。

 

PDO的prepared statement:

复制代码
<?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$pdo = new PDO("mysql:host=10.117.146.21:8306;dbname=springdemo", 'root', '[password]');

$prepared = $pdo->prepare('select nickname from user where id = :id');

$prepared->execute(array(':id' => $_GET['id']));

while ($results = $prepared->fetch(PDO::FETCH_ASSOC)) {
    print_r($results['nickname'] . '<br/>');
}

$pdo=null;
?>
复制代码

实验:

复制代码
http://localhost:8080/test.php?id=3

PHP version:5.5.30
micro

http://localhost:8080/test.php?id=3%20or%201=1

PHP version:5.5.30
micro
复制代码

都不再出现sql注入的威胁。

PDO里面多次用到fetch:

说明
PDO::FETCH_ASSOC 关联数组形式。
PDO::FETCH_NUM 数字索引数组形式。
PDO::FETCH_BOTH 两者数组形式都有,这是默认的。
PDO::FETCH_OBJ 按照对象的形式,类似于以前的mysql_fetch_object()函数。
PDO::FETCH_BOUND 以布尔值的形式返回结果,同时将获取的列值赋给bindParam()方法中指定的变量。
PDO::FETCH_LAZY 以关联数组、数字索引数组和对象3种形式返回结果。

把上面程序给prepared statement传参数的过程改了一下:

复制代码
<?php
header('Content-Type: text/html; charset=utf-8');
echo "PHP version:" . PHP_VERSION . "<br/>";

$pdo = new PDO("mysql:host=10.117.146.21:8306;dbname=springdemo", 'root', '[password]');

$prepared = $pdo->prepare('select nickname from user where id = :id');

$prepared->bindParam(':id', $_GET['id']);
$prepared->execute();

while ($results = $prepared->fetch(PDO::FETCH_ASSOC)) {
    print_r($results['nickname'] . '<br/>');
}

$pdo=null;
?>
复制代码

实验之后,结果对于上面哪些url,都能得到正确的结果。

性能方面:

PDO和MySQLi都有非常好的性能。在非prepared statements的基准测试下,MySQLi略快2.5%,而prepared statements下是6.5%,可以说对于性能无关紧要。
如果你真的非常介意这一点点性能的话,而自带的MySQL扩展比两者都快,你可以考虑下它。

 

上面部分来自这篇:http://blog.csdn.net/yipiankongbai/article/details/17277477 《PDO vs. MySQLi 选择哪一个?》

posted @   blcblc  阅读(2209)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示