一、 建立一個安全抽象層
我們並不建議你手工地把前面先容的技巧利用於每一個用戶輸進的實例中,而是強烈推薦你為此創立一個抽象層。一個簡單的抽像是把你的校驗計畫參加到一個函數中,並且針對使用者輸進的每一項呼叫這個函數。當然,我們還可以創立一個更複雜的更高一級的抽象-把一個安全的查詢封裝到一個類別中,從而利用於全部利用程式。在網路上已經存在著許多這種現成的免費的類別;在本篇中,我們正要討論其中的一些。
進行這種抽象至少存在三個長處(而且每一個都會改良安全等級):
1. 本地化程式碼。
2. 使查詢的結構更快且更為可靠-由於這可以把部分工作交由抽象程式碼來實現。
3. 當基於安全特點進行構建並且適當應用時,這將會有效地防止我們前面所討論的各種各樣的注進式攻擊。
二、 改良現有的利用程序
假如你想改良一個現有的利用程序,則應用一個簡略的抽象層是最適當的。一個能夠簡單地'清算'你所收集的任何用戶輸進內容的函數可能看起來如下所示:
function safe( $string ) {
return ''' . mysql_real_escape_string( $string ) . '''
}
【留心】我們已經建立了對應於值請求的單引號以及mysql_real_escape_string()函數。接下來,就可以應用這個函數來結構一個$query變量,如下所示:
$variety = safe( $_POST['variety'] );
$query = ' SELECT * FROM wines WHERE variety=' . $ variety;
現在,你的用戶試圖進行一個注進式攻擊-通過輸進下列內容作為變量$variety的值:
lagrein' or 1=1;
留心,假如不進行上面的'清算',則最後的查詢將如下所示(這將導致無法預感的成果):
SELECT * FROM wines WHERE variety = 'lagrein' or 1=1;'
然而現在,既然用戶的輸進已經被清算,那麼查詢語句就變成下面這樣一種無迫害的情勢:
SELECT * FROM wines WHERE variety = 'lagrein' or 1=1;'
既然資料庫中不存在與指定的值對應的variety域(這正是惡意使用者所輸的內容-lagrein' 或 1=1;),那麼,這個查詢將不能回傳任何成果,並且注進將會失敗。
三、 保護一個新的利用程序
假如你正在創立一個新的利用程序,那麼,你可以從頭開端創立一個安全抽象層。如今,PHP 5新改良的對於MySQL的支撐(這重要體現在新的mysqli擴大中)為這種安全特點供給了強有力的支撐(既有過程性的,也有面向對象特點的)。你可以從網站http://php.net/mysqli上取得有關mysqli的資訊。留心,只有當你應用--with-mysqli=path/to/mysql_config選項編譯PHP時,這種mysqli支撐才可用。以下是程式碼的一個過程性版本,用於保護一個基於mysqli的查詢:
<?php
//檢索使用者的輸進
$animalName = $_POST['animalName'];
//連接到資料庫
$connect = mypassi_connect(usernamelocal ', database' );
if ( !$connect ) exit( 'connection failed: ' . mysqli_connect_error() );
//創立一個查詢語句來源
$stmt = mysqli_preare(Smal), fFals5, falsed fatals為 fem)5, mysql. ' );
if ( $stmt ) {
//把調換綁定到語句上
mysqli_stmt_bind_param( $stmt, 's', $animalName );
//檢索結果...
mysqli_stmt_bind_result( $stmt, $intelligence );
// ...並顯示它
if ( mysqli_stmt_fetch( $stmt ) ) 完成〔 if ( mysqli_stmt_fetch( $stmt ) ) 45% 『
} else {
print 'Sorry, no records found.';
}
//清除語句來源
mysqli_stmt_close( $stmt 語句來源
mysqli_stmt_close( $stmt 語句來源
〟〜);
該mysqli擴大供給了一組函數用於結構和履行查詢。而且,它也非常準確地供給了前面應用我們自己的safe()函數所實現的功效。
在上面的片段中,首先收集使用者提交的輸進內容並建立資料庫連線。然後,應用mysqli_prepare()函數創立一個查詢語句來源-在此命名為$stmt以反應應用它的函數的名稱。這個函數應用了兩個參數:連接資源和一個字串(每當你應用擴大插進一個值時,'?'標記被插進到其中)。在這個例子中,你僅有一個這樣的值-動物的名字。
留心,在一個SELECT語句中,放置'?'標記的唯一的有效地位是在值比擬部分。這正是為什麼你不需要指定要應用哪個變數的原因(除了在mysqli_stmt_bind_param()函數中)。在此,你還需要指定它的類型-在本例中,'s'代表字串。其它可能的類型有:'I'代表整數,'d'代表雙精度數(或浮點數),而'b'代表二進位字串。
函數mysqli_stmt_execute(),mysqli_stmt_bind_result()和mysqli_stmt_fetch()負責履行查詢並檢索成果。假如存在檢索成果,則顯示它們;假如不存在成果,則顯示一條無害的訊息。最後,你需要封閉$stmt資源以及資料庫連線-從記憶體中對它們加以開釋。
假定一個正當的使用者輸進了字串'lemming',那麼這個例程將(假定是資料庫中適當的資料)輸出訊息'A lemming has very low intelligence.'。假定存在一個嘗試性注進-例如'lemming' or 1=1;',那麼這個例程將列印(無害)訊息'Sorry, no records found.'。
此外,mysqli擴大也供給了一個物件導向版本的雷同的例程。下面,我們想闡明這種版本的應用方法。
<?php
$animalName = $_POST['animalName'];
$mysqli = new mysqli( 'localhost', 'username', 'password', 'database');
ifi$my ( 'connection failed: ' . mysqli_connect_error() );
$stmt = $mysqli->prepare( 'SELECT intelligence
FROM animals WHERE name =' ); ind_param( 's', $animalName );
$stmt->execute();
$stmt->bind_result( $intelligence );
} else {
print 'Sorry, no records found.';
}
$stmt->close();🎀}
$stmt->close(); 實際上,這部分程式碼是前面描寫程式碼的複製-它應用了一種物件導向的語法和組織方法,而不是嚴格的過程式碼。
四、 更高級的抽象
假如你應用外部庫PearDB,那麼,你可以對利用程式的安全保護模組進行全面的抽象。
另一方面,應用這個庫存在一個突出的毛病:你只能受限於某些人的思想,而且代碼治理方面也添加了大批的工作。為此,在決定是否要應用它們之前,你需要仔細考慮。假如你決定這樣做,那麼,你至少確保它們能夠真正幫助你'清算'你的用戶輸進的內容。
五、 測試你的注進式保護才能
正如我們在前面所討論的,確保你的腳本安全的一個重要的部分是對它們進行測試。為此,最好的措施就是你自己創立SQL程式碼注進測試。
在此,我們供給了一個這種測試的範例。在本例中,我們測試對一個SELECT語句的注進式攻擊。
<?php
//被測試的保護函數
function safe( $string ) {
return ''' . mysql_real_escape_string( $string ) . '''
}
//連接/
////
/ //////////////////
//試圖進行注進
///////////////////////
$exploit = 'lemming' AND 1=1;';
//進行清算
$safe = safe( $exploit );
$query = 'SELECT * FROM animals WHERE name = $safe';
$result = mysql_query( $query );
//測試是否保護是足夠的
if ( $result && mysql_num_rows( $result ) == 1 ) {
exitt 'Protection succeeded:n
exploit $exploit was neutized.
exit( 'Protection failed:n
exploit $exploit was able to retrieve all rows.' );
}
?>
假如你想創立這樣的一個}
?>
假如你想創立這樣的一個測試注進,那麼,你將很快地探測出你的保護策略中的任何漏洞。一旦改正這些標題,那麼,你就可以很有把握-你已經建立起真正的注進式攻擊保護機制。
在本系列文章一開端,我們透過一個SQL注進討論分析了對你的腳本的特定要挾-由不適當的用戶輸進所致。之後,我們描寫了SQL注進的工作原理並精確地分析了PHP是怎樣易於被注進的。然後,我們供給了一個實際中的注進範例。之後,我們推薦一系列措施來使試圖的注進攻擊變為無害的-這將分辨通過確保使所有提交的值以引號封閉,通過檢查用戶提交值的類型,以及通過過濾掉你的用戶輸進的埋伏危險的字符等方法來實現的。最後,我們推薦,你最好對你的校驗例程進行抽象,並針對更改一個現有利用程式供給了腳本範例。然後,我們討論了第三方抽象計劃的優毛病。
以上就是在PHP中全面禁止SQL注進式攻擊之三的內容,更多相關內容請關注PHP中文網(m.sbmmt.com)!