国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > PHP > 正文

PHP用PDO防Sql注入注意事項(xiàng)

2024-05-04 21:48:14
字體:
供稿:網(wǎng)友

防注入是程序員一個(gè)必須要了解的基本安全知道,下面我來介紹關(guān)于php使用pdo時(shí)的一些防注入安全知識(shí),希望此教程對(duì)你會(huì)有所幫助.

在PHP 5.3.6及以前版本中,并不支持在DSN中的charset定義,而應(yīng)該使用PDO::MYSQL_ATTR_INIT_COMMAND設(shè)置初始SQL,即我們常用的 set names gbk指令.

為何PDO能防SQL注入?請(qǐng)先看以下PHP代碼:

  1. <?php 
  2. $pdo = new PDO("mysql:host=192.168.0.1;dbname=test;charset=utf8","root"); 
  3.  
  4. $st = $pdo->prepare("select * from info where id =? and name = ?"); 
  5.  
  6. $id = 21; 
  7.  
  8. $name = 'zhangsan'
  9.  
  10. $st->bindParam(1,$id); 
  11.  
  12. $st->bindParam(2,$name); 
  13. //開源代碼Vevb.com 
  14. $st->execute(); 
  15.  
  16. $st->fetchAll(); 
  17. ?> 

環(huán)境如下:PHP 5.4.7,Mysql 協(xié)議版本 10,MySQL Server 5.5.27

以上代碼,PHP只是簡(jiǎn)單地將SQL直接發(fā)送給MySQL Server,其實(shí),這與我們平時(shí)使用mysql_real_escape_string將字符串進(jìn)行轉(zhuǎn)義,再拼接成SQL語句沒有差別,只是由PDO本地驅(qū)動(dòng)完成轉(zhuǎn)義的,顯然這種情況下還是有可能造成SQL注入的,也就是說在php本地調(diào)用pdo prepare中的mysql_real_escape_string來操作query,使用的是本地單字節(jié)字符集,而我們傳遞多字節(jié)編碼的變量時(shí),有可能還是會(huì)造成SQL注入漏洞(php 5.3.6以前版本的問題之一,這也就解釋了為何在使用PDO時(shí),建議升級(jí)到php 5.3.6+,并在DSN字符串中指定charset的原因.

針對(duì)php 5.3.6以前版本,以下代碼仍然可能造成SQL注入問題:

  1. $pdo->query('SET NAMES GBK'); 
  2.  
  3. $var = chr(0xbf) . chr(0x27) . " OR 1=1 /*"
  4.  
  5. $query = "SELECT * FROM info WHERE name = ?"
  6.  
  7. $stmt = $pdo->prepare($query); 
  8.  
  9. $stmt->execute(array($var)); 

原因與上面的分析是一致的.

而正確的轉(zhuǎn)義應(yīng)該是給mysql Server指定字符集,并將變量發(fā)送給MySQL Server完成根據(jù)字符轉(zhuǎn)義.

那么,如何才能禁止PHP本地轉(zhuǎn)義而交由MySQL Server轉(zhuǎn)義呢?

PDO有一項(xiàng)參數(shù),名為PDO::ATTR_EMULATE_PREPARES,表示是否使用PHP本地模擬prepare,此項(xiàng)參數(shù)默認(rèn)值未知,php 5.3.6+默認(rèn)還是使用本地變量轉(zhuǎn),拼接成SQL發(fā)送給MySQL Server的,我們將這項(xiàng)值設(shè)置為false,試試效果,如以下代碼:

  1. <?php 
  2.  
  3. $pdo = new PDO("mysql:host=192.168.0.1;dbname=test;","root"); 
  4.  
  5. $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 
  6.  
  7. $st = $pdo->prepare("select * from info where id =? and name = ?"); 
  8.  
  9. $id = 21; //m.survivalescaperooms.com 
  10.  
  11. $name = 'zhangsan'
  12.  
  13. $st->bindParam(1,$id); 
  14.  
  15. $st->bindParam(2,$name); 
  16.  
  17. $st->execute(); 
  18.  
  19. $st->fetchAll(); 
  20.  
  21. ?> 

這次PHP是將SQL模板和變量是分兩次發(fā)送給MySQL的,由MySQL完成變量的轉(zhuǎn)義處理,既然變量和SQL模板是分兩次發(fā)送的,那么就不存在SQL注入的問題了,但需要在DSN中指定charset屬性,代碼如下:

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root');

如此,即可從根本上杜絕SQL注入的問題.

使用PDO的注意事項(xiàng)

知道以上幾點(diǎn)之后,我們就可以總結(jié)使用PDO杜絕SQL注入的幾個(gè)注意事項(xiàng):

1.php升級(jí)到5.3.6+,生產(chǎn)環(huán)境強(qiáng)烈建議升級(jí)到php 5.3.9+ php 5.4+,php 5.3.8存在致命的hash碰撞漏洞.

2.若使用php 5.3.6+,請(qǐng)?jiān)谠赑DO的DSN中指定charset屬性.

3.如果使用了PHP 5.3.6及以前版本,設(shè)置PDO::ATTR_EMULATE_PREPARES參數(shù)為false(即由MySQL進(jìn)行變量處理),在DSN中指定charset是無效的,同時(shí)set names <charset>(此處詳細(xì)語句PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')的執(zhí)行是必不可少的,php 5.3.6以上版本已經(jīng)處理了這個(gè)問題,無論是使用本地模擬prepare還是調(diào)用mysql server的prepare均可.

4.如果使用了PHP 5.3.6及以前版本,因Yii框架默認(rèn)并未設(shè)置ATTR_EMULATE_PREPARES的值,請(qǐng)?jiān)跀?shù)據(jù)庫配置文件中指定emulatePrepare的值為false。

那么,有個(gè)問題,如果在DSN中指定了charset,是否還需要執(zhí)行set names <charset>呢?

是的,不能省。set names <charset>其實(shí)有兩個(gè)作用:

A.告訴mysql server, 客戶端(PHP程序)提交給它的編碼是什么

B.告訴mysql server, 客戶端需要的結(jié)果的編碼是什么

也就是說,如果數(shù)據(jù)表使用gbk字符集,而PHP程序使用UTF-8編碼,我們?cè)趫?zhí)行查詢前運(yùn)行set names utf8, 告訴mysql server正確編碼即可,無須在程序中編碼轉(zhuǎn)換。這樣我們以u(píng)tf-8編碼提交查詢到mysql server, 得到的結(jié)果也會(huì)是utf-8編碼。省卻了程序中的轉(zhuǎn)換編碼問題,不要有疑問,這樣做不會(huì)產(chǎn)生亂碼。

那么在DSN中指定charset的作用是什么? 只是告訴PDO,本地驅(qū)動(dòng)轉(zhuǎn)義時(shí)使用指定的字符集(并不是設(shè)定mysql server通信字符集),設(shè)置mysql server通信字符集,還得使用set names <charset>指令。

以下是一段可防注入的示例代碼:

  1. $dbhost="localhost"
  2. $dbname="test"
  3. $dbusr="root"
  4. $dbpwd=""
  5. $dbhdl=NULL; 
  6. $dbstm=NULL; 
  7.  
  8. $opt = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',);  
  9. $dsn='mysql:host=' . $dbhost . ';dbname=' . $dbname.';charset=utf8'
  10. try { 
  11.  $dbhdl = new PDO($dsn$dbusr$dbpwd$opt);//www.111cn.net 
  12.  $dbhdl=->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 
  13.  //dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT);//Display none 
  14.  //dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);//Display warning 
  15.  $dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);//Display exception 
  16. } catch (PDOExceptsddttrtion $e) {//return PDOException 
  17.  print "Error!: " . $e->getMessage() . "<br>"
  18.  die(); 
  19.  
  20.  
  21. $dbhost="localhost"
  22. $dbname="test"
  23. $dbusr="root"
  24. $dbpwd=""
  25. $dbhdl=NULL; 
  26. $dbstm=NULL; 
  27.  
  28. $dsn='mysql:host=' . $dbhost . ';dbname=' . $dbname.';charset=utf8'
  29. try { 
  30.  $dbhdl = new PDO($dsn$dbusr$dbpwd,); 
  31.  $dbhdl=->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 
  32.  //dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT);//Display none 
  33.  //dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_WARNING);//Display warning 
  34.  $dbhdl->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);//Display exception 
  35.  $dbhdl->query('SET NAMES GBK');  
  36. } catch (PDOExceptsddttrtion $e) {//return PDOException 
  37.  print "Error!: " . $e->getMessage() . "<br>"
  38.  die(); 

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 民乐县| 仙居县| 普洱| 广丰县| 沙雅县| 曲靖市| 静宁县| 信宜市| 德兴市| 平定县| 神木县| 宜阳县| 西乌| 新巴尔虎右旗| 中山市| 锦屏县| 株洲市| 双牌县| 舞阳县| 临泽县| 盐城市| 五大连池市| 武乡县| 萝北县| 台东市| 新邵县| 赤城县| 清徐县| 济南市| 江油市| 镇康县| 新乡县| 浦江县| 井冈山市| 内黄县| 东乌珠穆沁旗| 杭锦旗| 苏尼特左旗| 泸溪县| 普格县| 陇西县|