利用Mysqli和PDO
產(chǎn)生原因主要就是一些數(shù)據(jù)沒(méi)有經(jīng)過(guò)嚴(yán)格的驗(yàn)證,然后直接拼接 SQL 去查詢。導(dǎo)致產(chǎn)生漏洞,比如:
$id = $_GET['id'];
$sql = "SELECT name FROM users WHERE id = $id";
因?yàn)闆](méi)有對(duì) $_GET[‘id’] 做數(shù)據(jù)類型驗(yàn)證,注入者可提交任何類型的數(shù)據(jù),比如 " and 1= 1 or " 等不安全的數(shù)據(jù)。如果按照下面方式寫,就安全一些。
$id = intval($_GET['id']);
$sql = "SELECT name FROM users WHERE id = $id";
把 id 轉(zhuǎn)換成 int 類型,就可以去掉不安全的東西。
驗(yàn)證數(shù)據(jù)
防止注入的第一步就是驗(yàn)證數(shù)據(jù),可以根據(jù)相應(yīng)類型進(jìn)行嚴(yán)格的驗(yàn)證。比如 int 類型直接通過(guò) intval 進(jìn)行轉(zhuǎn)換就行:
$id = intval($_GET['id']);
字符處理起來(lái)比較復(fù)雜些,首先通過(guò) sprintf 函數(shù)格式化輸出,確保它是一個(gè)字符串。然后通過(guò)一些安全函數(shù)去掉一些不合法的字符,比如:
$str = addslashes(sprintf("%s",$str));
也可以用mysqli_real_escape_string 函數(shù)替代 addslashes 這樣處理以后會(huì)比較安全。當(dāng)然還可以進(jìn)一步去判斷字符串長(zhǎng)度,去防止 [緩沖區(qū)溢出攻擊] 比如:
$str = addslashes(sprintf("%s",$str));
$str = substr($str,0,40); //最大長(zhǎng)度為40
參數(shù)化綁定
參數(shù)化綁定,防止 SQL 注入的又一道屏障。php MySQLi 和 PDO 均能提供這樣的功能。比如 MySQLi 可以這樣去查詢:
$mysqli = new mysqli('localhost','my_user','my_password','world');
$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?,?,?,?)");
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
$stmt->bind_param('sssd',$code,$language,$official,$percent);
PDO 的更是方便,比如:
/* Execute a prepared statement by passing an array of values */
$sql = 'SELECT name,color,calories
FROM fruit
WHERE calories < :calories AND color = :colour';
$sth = $dbh->prepare($sql,array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
$red = $sth->fetchAll();
$sth->execute(array(':calories' => 175, ':colour' => 'yellow'));
$yellow = $sth->fetchAll();
我們多數(shù)使用 php 的框架進(jìn)行編程,所以最好不要自己拼寫 SQL,按照框架給定參數(shù)綁定進(jìn)行查詢。遇到較為復(fù)雜的 SQL 語(yǔ)句,一定要自己拼寫的時(shí)候,一定要注意嚴(yán)格的判斷。沒(méi)有用 PDO 或者 MySQLi 也可以自己寫個(gè) prepared,比如 wordpress db 查詢語(yǔ)句,可以看出也是經(jīng)過(guò)嚴(yán)格的類型驗(yàn)證。
function prepare( $query, $args ){
if(is_null($query))
return;
// This is not meant to be foolproof --
// but it will catch obviously incorrect usage.
if(strpos($query,'%') === false){
_doing_it_wrong('wpdb::prepare',sprintf(__('The query argument of %s must have a placeholder.'),'wpdb::prepare()'),'3.9');
}
$args = fun_get_args();
array_shift($args);
// If args were passed as an array (as in vsprintf), move them up
if(isset($args[0]) && is_array($args[0]))
$args = $args[0];
$query = str_replace("'%s'",'%s',$query);
//in case someone mistakenly already singlequoted it
$query = str_replace('"%s"','%s',$query);
//doublequote unquoting
$query = preg_replace('|(?<!%)%f|','%F',$query);
//Force floats to be locale unaware
$query = pref_replace('|(?<!%)%s|',"'%s'",$query);
//quote the strings,avoiding escaped strings like %%s
array_walk($args,array($this,'escape_by_ref'));
return @ vsprintf($query,$args);
}
總結(jié)
安全性很重要,也可以看出一個(gè)人基本功,項(xiàng)目漏洞百出,擴(kuò)展性和可維護(hù)性再好也沒(méi)有用。平時(shí)多留意,樹立安全意識(shí),養(yǎng)成一種習(xí)慣,一些基本的安全當(dāng)然也不會(huì)占用 coding 的時(shí)間。
養(yǎng)成這個(gè)習(xí)慣,即便在項(xiàng)目急,時(shí)間短的情況下,依然可以做的質(zhì)量很高,不要等到自己以后負(fù)責(zé)的東西,數(shù)據(jù)庫(kù)都被拿走了,造成損失才重視。
雖然國(guó)內(nèi)很多PHP程序員仍在依靠addslashes防止SQL注入,還是建議大家加強(qiáng)中文防止SQL注入的檢查。addslashes的問(wèn)題在于黑客可以用0xbf27來(lái)代替單引號(hào),而addslashes只是將0xbf27修改為0xbf5c27,成為一個(gè)有效的多字節(jié)字符,其中的0xbf5c仍會(huì)被看作是單引號(hào),所以addslashes無(wú)法成功攔截。
當(dāng)然addslashes也不是毫無(wú)用處,它是用于單字節(jié)字符串的處理,多字節(jié)字符還是用mysql_real_escape_string吧。
另外對(duì)于php手冊(cè)中g(shù)et_magic_quotes_gpc的舉例:
if(!get_magic_quotes_gpc()){
$lastname = addslashes($_POST[‘lastname’]);
}else{
$lastname = $_POST[‘lastname’];
}
最好對(duì)magic_quotes_gpc已經(jīng)開放的情況下,還是對(duì)$_POST[‘lastname’]進(jìn)行檢查一下。
再說(shuō)下mysql_real_escape_string和mysql_escape_string這2個(gè)函數(shù)的區(qū)別:
mysql_real_escape_string 必須在(PHP >= 4.3.0,PHP5)的情況下才能使用。否則只能用mysql_escape_string,兩者的區(qū)別是:mysql_real_escape_string 考慮到連接的當(dāng)前字符集,而mysql_escape_string 不考慮。
總結(jié)一下:
addslashes() 是強(qiáng)行加;
mysql_real_escape_string() 會(huì)判斷字符集,但是對(duì)PHP版本有要求;
mysql_escape_string不考慮連接的當(dāng)前字符集。
參考文章鏈接:
https://mp.weixin.qq.com/s/X6q0xpbnJ9lojIIwNmDzdA
補(bǔ)充:
一:addslashes
php addslashes函數(shù)的作用是在預(yù)定義的字符前面加上反斜杠,這些預(yù)定義字符包括:
單引號(hào)(')
雙引號(hào)(")
反斜杠(\)
NULL
$str="my name's wxp";
echo addslashes($str);//輸出my name\'s wxp
然后在拼接mysql字符串:
$sql="insert into student(student_name)values('".addslashes($str)."')";
mysql_query($sql);
此時(shí)字符串被插入到數(shù)據(jù)庫(kù),那么大家是否知道插入的字符串是帶反斜杠還是不帶反斜杠呢?恐怕很多人都會(huì)認(rèn)為肯定是帶反斜杠的字符串。其實(shí)這個(gè)答案是錯(cuò)誤的,插入的字符串是沒(méi)有帶反斜杠。至于為什么插入的字符串在數(shù)據(jù)庫(kù)中是沒(méi)有加反斜杠,請(qǐng)大家繼續(xù)看下面講解。
如果字符串$str="my name’s wxp"是使用POST和GET提交的數(shù)據(jù),這個(gè)時(shí)候插入數(shù)據(jù)庫(kù)中的數(shù)據(jù)是帶反斜杠的,由此可知addslashes只是在POST和GET數(shù)據(jù)插入數(shù)據(jù)庫(kù)時(shí)才會(huì)把反斜杠同時(shí)插入到數(shù)據(jù)庫(kù),其他情況下不會(huì)將反斜杠插入到數(shù)據(jù)庫(kù)。
注釋:默認(rèn)地,PHP 對(duì)所有的 GET、POST 和 COOKIE 數(shù)據(jù)自動(dòng)運(yùn)行 addslashes()。所以您不應(yīng)對(duì)已轉(zhuǎn)義過(guò)的字符串使用 addslashes(),因?yàn)檫@樣會(huì)導(dǎo)致雙層轉(zhuǎn)義。遇到這種情況時(shí)可以使用函數(shù) get_magic_quotes_gpc() 進(jìn)行檢測(cè)
參考:
原文鏈接:https://blog.csdn.net/MengJing_/article/details/89353942
二:mysql_real_escape_string
mysql_real_escape_string
(PHP 4 >= 4.3.0, PHP 5)
mysql_real_escape_string — 將字符串中的特殊字符進(jìn)行轉(zhuǎn)義,以在 SQL 語(yǔ)句中使用
警告 本擴(kuò)展自 PHP 5.5.0 起已廢棄,并在自 PHP 7.0.0 開始被移除。應(yīng)使用 MySQLi 或 PDO_MySQL
擴(kuò)展來(lái)替換之。參見 MySQL:選擇 API 指南來(lái)獲取更多信息。用以替代本函數(shù)的有:
mysqli_real_escape_string() PDO::quote()
說(shuō)明
mysql_real_escape_string(string $unescaped_string, resource $link_identifier = NULL): string
考慮到連接的當(dāng)前字符集,對(duì) unescaped_string 進(jìn)行特殊字符轉(zhuǎn)義,以便安全地將其放置在 mysql_query() 中。如果要插入二進(jìn)制數(shù)據(jù),則必須使用此函數(shù)。
mysql_real_escape_string() 調(diào)用 mysql 庫(kù)的函數(shù) mysql_real_escape_string, 在以下字符前添加反斜線:\x00、\n、\r、\、'、" 和 \x1a.
在向 MySQL 發(fā)送查詢之前,必須始終(有少數(shù)例外)使用此函數(shù)來(lái)確保數(shù)據(jù)的安全。
警告
安全: 默認(rèn)字符集 The character set must be set either at the server level,or with the API function mysql_set_charset() for it to affect mysql_real_escape_string(). See the concepts section on character sets for more information.
示例:
<?php
// Connect
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(mysql_error());
// Query
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>
三:mysql_escape_string
mysql_escape_string
(PHP 4 >= 4.0.3, PHP 5)
mysql_escape_string — 轉(zhuǎn)義字符串用于 mysql_query
警告 本函數(shù)自 PHP 4.3.0 起已廢棄,并且它和整個(gè) MySQL 擴(kuò)展自 PHP 7.0.0 開始被移除。 可以選擇出于活躍開發(fā)中的MySQLi 或 PDO_MySQL 擴(kuò)展來(lái)作為替代。 參見 MySQL:選擇 API 指南來(lái)獲取更多信息。用以替代本函數(shù)的有:mysqli_escape_string() PDO::quote()
說(shuō)明
mysql_escape_string(string $unescaped_string): string
本函數(shù)將轉(zhuǎn)義 unescaped_string,使之可以安全用于 mysql_query()。此函數(shù)已棄用。
本函數(shù)和 mysql_real_escape_string() 相同,除了 mysql_real_escape_string() 接受連接處理程序并根據(jù)當(dāng)前字符集進(jìn)行轉(zhuǎn)義。mysql_escape_string() 不接受連接參數(shù),也不遵循當(dāng)前字符集設(shè)定。
示例
<?php
$item = "Zak's Laptop";
$escaped_item = mysql_escape_string($item);
printf("Escaped string: %s\n", $escaped_item);
?>
以上示例會(huì)輸出:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-731223.html
Escaped string: Zak\'s Laptop
注意:
mysql_escape_string() 不轉(zhuǎn)義 % 和 _。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-731223.html
到了這里,關(guān)于PHP常見的SQL防注入方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!