首頁 >後端開發 >php教程 >用PHP 4.2書寫安全的腳本_PHP教程

用PHP 4.2書寫安全的腳本_PHP教程

WBOY
WBOY原創
2016-07-21 16:09:00916瀏覽

原文:Kevin Yank  轉自:www.linuxforum.net (恭喜再此開通)

在很長一段時間內,PHP作為伺服器端腳本語言的最大賣點之一就是會為從表單提交的值自動建立一個全域變數。在PHP 4.1中,PHP的製作者們推薦了一個存取提交資料的替代手段。在PHP 4.2中,他們取消了那種老舊的做法!正如我將在這篇文章中解釋的那樣,作出這樣的變化的目的是出於安全性的考慮。我們將研究PHP在處理表單提交及其它資料時的新的做法,並說明為什麼這樣做會提高程式碼的安全性。 

這裡有什麼錯誤?

看看下面的這段PHP腳本,它用來在輸入的用戶名及口令正確時授權訪問一個Web頁: 
// 檢查用戶名及口令 
if ($username == 'kevin' and $password == 'secret') 
$authorized = true; 
?> 
?> 
 

Please enter your username and password:

 
" method="POST"> 

Username: 
 
Password: 
 

 
 
 
 
 
OK,我相信大約半數的讀者會不屑的說「太愚蠢了-- 我不會犯這樣的錯誤的!」但是我保證有很多的讀者會想「嗨,沒什麼問題啊,我也會這麼寫的!」當然還會有少數人會對這個問題感到困惑(“什麼是PHP?”)。 PHP被設計為一個「好的、容易的」腳本語言,初學者可以在很短的時間內學會使用它;它也應該能夠避免初學者犯下上面的錯誤。 
再回到剛才的問題,上面的程式碼中存在的問題是你可以輕易地獲得存取的權力,而不需要提供正確的使用者名稱和口令。只在要你的瀏覽器的網址列的最後加上?authorized=1。因為PHP會自動地為每一個提交的值創建一個變數 -- 不論是來自動一個提交的表單、URL查詢字串還是一個cookie -- 這會將$authorized設定為1,這樣一個未授權的使用者也可以突破安全限制。 
那麼,怎麼簡單地解決這個問題呢?只要在程式的開頭將$authorized預設為false。這個問題就不存在了! $authorized是一個完全在程式碼中建立的變數;但是為什麼開發者得為每個惡意的使用者提交的變數擔心呢? 

PHP 4.2作了什麼改變?

在PHP 4.2中,新安裝的PHP中的register_globals選項預設為關閉,因此EGPCS值(EGPCS是Environment、Get、Post、Cookies、Server的縮寫 -- 這是PHP中外部變數來源的全部範圍)不會被當作全域變數來創建。當然,這個選項還可以透過手動來開啟,但PHP的開發者推薦你將其關閉。要貫徹他們的意圖,你需要使用其它的方法來取得這些值。
從PHP 4.1開始,EGPCS值就可以從一組指定的陣列中獲得: 
$_ENV -- 包含系統環境變數 
$_GET -- 包含查詢方法中的變量,以及提交方法字串為GET的表單中的變數 
$_POST -- 包含提交方式為POST的表單中的變數 
$_COOKIE -- 包含所有cookie變數 
$_SERVER --@包含變數,例如所有cookie變數 
$_SERVER ---包含變數,例如>$_REQUEST -- 包含$_GET、$_POST和$_COOKIE的全部內容 
$_SESSION -- 包含所有已註冊的session變數 在PHP 4.1之前,當開發者關閉register_globals選項(這也被考慮在提高PHP性能的一種方法)後,必須使用諸如$HTTP_GET_VARS這樣的令人討厭的名字來獲取這些變數。這些新的變數名稱不僅短,而且它們還有其他優點。
首先,讓我們在PHP 4.2中(也就是說關閉register_globals 選項)重寫上面提到的程式碼: 
$username = $_REQUEST['username']; 
$username = $_REQUEST['username']; 
$password = $_REQUEST['password']; 

// 檢查使用者名稱及口令 
if ($username == 'kevin and $password == 'secret')>?> 
 
 

Please enter username and password:

 
" method="POST"> 

Username: 
 
Password: 
 

 
 
form> 
 
 
 
如你所看到的,我所需要?php endif; ?> 
如你所看到的,我所需要?php endif; ?> 
如你所看到的,我所需要?php endif; ?> 
如你所看到的,我所需要做的只是在程式碼的開始增加下面兩行: 
$username = $_REQUEST['username']; 
$password = $_REQUEST['password']; 
因為我們希望使用者名稱和密碼是由使用者提交的,所以我們從$_REQUEST數組中取得這些值。使用這個陣列使得使用者可以自由選擇傳遞方式:透過URL查詢字串(例如允許使用者建立書籤時自動輸入他們的憑證)、透過一個提交的表單或是透過一個cookie。如果你想要限制只能透過表單提交證書(更精確地說,是透過HTTP POST請求),你可以使用$_POST陣列: 
$username = $_POST['username']; 
$password = $_POST['password']; 
除了「引入」這兩個變數以外,程式碼沒有改變。簡單地關閉register_globals選項促使開發者更進一步了解哪些資料是來自外部的(不可信任的)資源。
請注意這裡還有一個小問題:PHP中預設的error_reporting設定仍然是E_ALL & ~E_NOTICE,因此如果「username」和「password」這兩個值沒有被提交,試圖從$_REQUEST數組或$_POST數組中獲得這兩個值並不會招致任何錯誤訊息。如晨不你的PHP程式需要嚴格的錯誤檢查,你還需要增加一些程式碼以先檢查這些變數。 
但這是否意味著更多的輸入? 是的,在像上面這樣的簡單程序中,使用PHP 4.2常常會增加輸入量。但是,還是看看光明的一面吧 -- 你的程式終究是更安全了! 不過認真的說,PHP的設計者並沒有完全忽略你的痛苦。在這些新數組中有一個特殊的其它所PHP變數都不具備的特徵,它們是完全的全域變數。這對你有什麼幫助呢?讓我們先對我們的範例進行擴充。
為了讓網站中的多個頁面可以使用使用者名稱/口令論證,我們將我們使用者認證程式寫到一個include檔案(protectme.php): 
function authorize_user($authuser, $authpass) 

$username = $_POST['username']; 
$username = $_POST['username']; 
$username = $_POST['username']; 
$密碼使用者名稱與口令 
if ($username != $authuser or $password != $authpass): 
?> 
 
很簡單,很清晰明了,對不對?現在是考驗你的眼力和經驗的時候了 -- 在authorize_user 函數中少了什麼? 
在函數中沒有申明$_POST是一個全域變數!在php 4.0中,當register_globals開啟時,你需要增加一行程式碼以在函數中取得$username和$password變數: 
function authorize_user($authuser, $authpass) 

$password; 
... 
在PHP中,和其它具有類似語法的語言不同,函數外的變數在函數中不能自動獲得,你需要像上面所說明的那樣增加一行以指定其來自global範圍。
在PHP 4.0中,當關閉register_globals以提供安全性時,你可以使用$HTTP_POST_VARS數組以獲得你的表單提交的值,但是你還是需要從全域範圍導入這個數組: 
function authorize_user($user authuser, $authpass) 

global $HTTP_POST_VARS; 
$username = $HTTP_POST_VARS['username” 4.1及以後的版本中,特殊的$_POST變數(以及上述的其它變數)可以在所有範圍內使用。這就是為什麼不需要在函數中申明$_POST變數是全域變數的原因: 
function authorize_user($authuser, $authpass) 

$username = $_POST

$username = $_POST

$username = $_POST['username']; >$password = $_POST['password']; 

這對session有什麼影響? 

特殊的$_SESSION陣列的引入實際上有助於簡化session程式碼。你不需要把session變數申明為全域變量,然後再去留意哪些變數被註冊了,你現在可以簡單地從$_SESSION['varname']引用你所有的session變數。 
現在讓我們來看看另一個使用者認證的範例。這次,我們使用sessions以標誌一個在你的網站繼續逗留的用戶已經經過了用戶認證。首先,讓我們來看看PHP 4.0版本(開啟register_globals): 
session_start(); 
if ($username == 'kevin' and$pass) >{ 
$authorized = true; 
session_register('authorized'); 

?> 
 
 
 當我們增加了我們的特殊的陣列(PHP 4.1)並關閉register_globals(PHP 4.2)後,我們的程式將是這樣的: 
session_start(); 
session_start(); 
if
if ($username == 'kevin' and $password == 'secret') 
$_SESSION['authorized'] = true; 
?> 
 
 
 
 
 
 
是不是更簡單了?你不再需要再將普通的變數註冊為一個session變量,你只需要直接設定session變數(在$_SESSION數組中),然後用同樣的方法使用它。程式變得更短了,而且對於什麼變數是session變數也不會造成混亂! 
總結 

在這篇文章中,我解釋了PHP腳本語言改變的深層原因。在PHP 4.1中,新增了一組特殊資料以存取外部資料。這些數組可以在任何範圍內調用,這使得外部資料的存取更方便。在PHP 4.2中,register_globals被預設關閉以鼓勵使用這些陣列以避免無經驗的開發者編寫出不安全的PHP程式碼。  http://www.bkjia.com/PHPjc/314671.htmlwww.bkjia.comtruehttp: //www.bkjia.com/PHPjc/314671.html
TechArticle原著:KevinYank轉自:www.linuxforum.net(恭喜再開通) 在很長此一段時間內,PHP作為伺服器端腳本語言的最大賣點之一就是會為從表單提交的值自...
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn