PHP防止sql语句注入终极解决方案(包含pdo各种操作使用实例)

完美解决方案就是使用拥有Prepared Statement机制(预处理sql)的PDO

//先做个实验 先不用预处理sql写法

  1. query('SELECT * FROM wz_admin WHERE id = '.$id);
  2. print_r($stmt -> fetchAll ());
  3. exit();

可以发现 可以输出数据 id=2的时候是没数据的

用预处理sql写法

  1. $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

它会告诉 PDO 禁用模拟预处理语句,并使用 real parepared statements 。这可以确保SQL语句和相应的值在传递到mysql服务器之前是不会被PHP解析的(禁止了所有可能的恶意SQL注入攻击)。

  1. $pdo = new PDO('mysql:dbname=testdatabase;host=localhost;charset=utf8', 'root', 'root');
  2. $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  3. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  4. $stmt = $pdo->prepare('SELECT * FROM wz_admin WHERE id = :id');
  5. $id='2 or 1=1';
  6. $stmt->execute(array('id' => $id));
  7. print_r($stmt -> fetchAll ());
  8. exit();

可以发现没有输出数据

上面这段代码就可以防范sql注入。为什么呢?

当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;
当调用到 execute()时,用户提交过来的值才会传送给数据库,它们是分开传送的,两者独立的,SQL攻击者没有一点机会

下面我简单的封装了下

  1. class Db
  2. {
  3. private static $pdo;
  4. public static function getPdo ()
  5. {
  6. if ( self::$pdo == null )
  7. {
  8. $host = 'localhost';
  9. $user = 'root';
  10. $pwd = '';
  11. $dbname = 'testdatebase';
  12. $dsn = "mysql:host=$host;dbname=$dbname;port=3306";
  13. $pdo = new PDO ( $dsn, $user, $pwd );
  14. $pdo->query('set names utf8;');
  15. $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  16. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  17. self::$pdo = $pdo;
  18. }
  19. return self::$pdo;
  20. }
  21. public static function getStmt($sql)
  22. {
  23. $pdo = self::getPdo();
  24. return $pdo->prepare($sql);
  25. }
  26. public static function getinsertids()
  27. {
  28. $pdo = self::getPdo();
  29. $insertid = $pdo->lastInsertId();
  30. return $insertid;
  31. }
  32. public static function SETATTR_AUTOCOMMIT()
  33. {
  34. $pdo = self::getPdo();
  35. $pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
  36. }
  37. public static function beginTransactions()
  38. {
  39. $pdo = self::getPdo();
  40. $pdo->beginTransaction();//开启事务处理
  41. }
  42. public static function commits()
  43. {
  44. $pdo = self::getPdo();
  45. $pdo->commit();//开启事务处理
  46. }
  47. public static function roolbacks()
  48. {
  49. $pdo = self::getPdo();
  50. $pdo->rollBack();//开启事务处理
  51. }
  52. }
  53. // examples
  54. $sql = "SELECT * FROM wz_admin WHERE id = ?";
  55. $stmt = Db::getStmt ( $sql );
  56. $stmt -> execute ( array (1));
  57. //PDO::FETCH_ASSOC这个参数代表只显示关联数组 默认是显示索引下标和关联数组的
  58. print_r($stmt -> fetchAll (PDO::FETCH_ASSOC));
  59. exit();
  60. //以下返回1 都是数据库操作成功的
  61. $sql = "INSERT INTO testss (wef,wef1) VALUES(?,?)";
  62. $stmt = Db::getStmt ( $sql );
  63. $dd=$stmt -> execute ( array ('wefe','wefe1'));
  64. print_r($dd);
  65. exit();
  66. $sql = "update testss set wef=? where id=2";
  67. $stmt = Db::getStmt ( $sql );
  68. $dd=$stmt -> execute ( array ('34'));
  69. print_r($dd);
  70. //事务 只有commit得而时候才会执行sql语句 如果过程中报错就会roolbacks 返回到初始状态
  71. DB::SETATTR_AUTOCOMMIT();
  72. $username=$_POST['username'];
  73. $sql = 'INSERT INTO arjianghu_register (username) VALUES(?)';
  74. $stmt = \Db::getStmt($sql);
  75. Db::beginTransactions();//开启事务处理
  76. $isOk = $stmt->execute(array($username));
  77. if(!$isOk){
  78. Db::roolbacks();
  79. echo json_encode(array('success'=>0,'msg'=>'网络繁忙','data'=>''));
  80. exit();
  81. }
  82. $sql = 'INSERT INTO arjianghu_zhuangtai (personid) VALUES(?)';
  83. $stmt = \Db::getStmt($sql);
  84. $isOk = $stmt->execute(array(1));
  85. if($isOk){
  86. echo json_encode(array('success'=>1,'msg'=>'操作成功','data'=>''));
  87. exit();
  88. }else{
  89. Db::roolbacks();
  90. echo json_encode(array('success'=>0,'msg'=>'网络繁忙','data'=>''));
  91. exit();
  92. }
  93. DB::commits();

结果 : 之所以预处理sql 能够防止注入式因为 他内部经过了转义等其他处理

结果 : pdo字段不能用?必须要变量 ?只能用在值

点赞 ( 0 )

0 条评论

发表评论

人生在世,错别字在所难免,无需纠正。

插入图片
s
返回顶部