用PHP書(shū)寫(xiě)安全的腳本代碼
更新時(shí)間:2012年02月05日 21:33:09 作者:
在很長(zhǎng)一段時(shí)間內(nèi),PHP作為服務(wù)器端腳本語(yǔ)言的最大賣(mài)點(diǎn)之一就是會(huì)為從表單提交的值自動(dòng)建立一個(gè)全局變量。在PHP 4.1中,PHP的制作者們推薦了一個(gè)訪(fǎng)問(wèn)提交數(shù)據(jù)的替代手段
在PHP 4.2中,他們?nèi)∠四欠N老的做法!正如我將在這篇文章中解釋的那樣,作出這樣的變化的目的是出于安全性的考慮。我們將研究PHP在處理表單提交及其它數(shù)據(jù)時(shí)的新的做法,并說(shuō)明為什么這樣做會(huì)提高代碼的安全性。
這里有什么錯(cuò)誤?
看看下面的這段PHP腳本,它用來(lái)在輸入的用戶(hù)名及口令正確時(shí)授權(quán)訪(fǎng)問(wèn)一個(gè)Web頁(yè)面:
<?php
// 檢查用戶(hù)名及口令
if ($username == 'kevin' and $password == 'secret')
$authorized = true;
?>
<?php if (!$authorized): ?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
OK,我相信大約半數(shù)的讀者會(huì)不屑的說(shuō)“太愚蠢了-- 我不會(huì)犯這樣的錯(cuò)誤的!”但是我保證有很多的讀者會(huì)想“嗨,沒(méi)什么問(wèn)題啊,我也會(huì)這么寫(xiě)的!”當(dāng)然還會(huì)有少數(shù)人會(huì)對(duì)這個(gè)問(wèn)題感到困惑(“什么是PHP?”)。PHP被設(shè)計(jì)為一個(gè)“好的而且容易的”腳本語(yǔ)言,初學(xué)者可以在很短的時(shí)間內(nèi)學(xué)會(huì)使用它;它也應(yīng)該能夠避免初學(xué)者犯上面的錯(cuò)誤。
再回到剛才的問(wèn)題,上面的代碼中存在的問(wèn)題是你可以很容易地獲得訪(fǎng)問(wèn)的權(quán)力,而不需要提供正確的用戶(hù)名和口令。只在要你的瀏覽器的地址欄的最后添加?authorized=1。因?yàn)镻HP會(huì)自動(dòng)地為每一個(gè)提交的值創(chuàng)建一個(gè)變量 -- 不論是來(lái)自動(dòng)一個(gè)提交的表單、URL查詢(xún)字符串還是一個(gè)cookie -- 這會(huì)將$authorized設(shè)置為1,這樣一個(gè)未授權(quán)的用戶(hù)也可以突破安全限制。
那么,怎么簡(jiǎn)單地解決這個(gè)問(wèn)題呢?只要在程序的開(kāi)頭將$authorized默認(rèn)設(shè)置為false。這個(gè)問(wèn)題就不存在了!$authorized是一個(gè)完全在程序代碼中創(chuàng)建的變量;但是為什么開(kāi)發(fā)者得為每一個(gè)惡意的用戶(hù)提交的變量擔(dān)心呢?
PHP 4.2作了什么改變?
在PHP 4.2中,新安裝的PHP中的register_globals選項(xiàng)默認(rèn)為關(guān)閉,因此EGPCS值(EGPCS是Environment、Get、Post、Cookies、Server的縮寫(xiě) -- 這是PHP中外部變量來(lái)源的全部范圍)不會(huì)被作為全局變量來(lái)創(chuàng)建。當(dāng)然,這個(gè)選項(xiàng)還可以通過(guò)手工來(lái)開(kāi)啟,但是PHP的開(kāi)發(fā)者推薦你將其關(guān)閉。要貫徹他們的意圖,你需要使用其它的方法來(lái)獲取這些值。
從PHP 4.1開(kāi)始,EGPCS值就可以從一組指定的數(shù)組中獲得:
$_ENV -- 包含系統(tǒng)環(huán)境變量
$_GET -- 包含查詢(xún)字符串中的變量,以及提交方法為GET的表單中的變量
$_POST -- 包含提交方式為POST的表單中的變量
$_COOKIE -- 包含所有cookie變量
$_SERVER -- 包含服務(wù)器變量,例如HTTP_USER_AGENT
$_REQUEST -- 包含$_GET、$_POST和$_COOKIE的全部?jī)?nèi)容
$_SESSION -- 包含所有已注冊(cè)的session變量
在PHP 4.1之前,當(dāng)開(kāi)發(fā)者關(guān)閉register_globals選項(xiàng)(這也被考慮為提高PHP性能的一種方法)后,必須使用諸如$HTTP_GET_VARS這樣的令人討厭的名字來(lái)獲取這些變量。這些新的變量名不僅僅短,而且它們還有其他優(yōu)點(diǎn)。
首先,讓我們?cè)赑HP 4.2中(也就是說(shuō)關(guān)閉register_globals 選項(xiàng))重寫(xiě)上面提到的代碼:
<?php
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
// 檢查用戶(hù)名和口令
if ($username == 'kevin' and $password == 'secret')
$authorized = true;
?>
<?php if (!$authorized): ?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
正如你看到的,我所需要做的只是在代碼的開(kāi)始增加下面兩行:
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
因?yàn)槲覀兿M脩?hù)名和密碼是由用戶(hù)提交的,所以我們從$_REQUEST數(shù)組中獲取這些值。使用這個(gè)數(shù)組使得用戶(hù)可以自由選擇傳遞方式:通過(guò)URL查詢(xún)字符串(例如允許用戶(hù)創(chuàng)建書(shū)簽時(shí)自動(dòng)輸入他們的證書(shū))、通過(guò)一個(gè)提交的表單或者是通過(guò)一個(gè)cookie。如果你想要限制只能通過(guò)表單提交證書(shū)(更精確地說(shuō),是通過(guò)HTTP POST請(qǐng)求),你可以使用$_POST數(shù)組:
$username = $_POST['username'];
$password = $_POST['password'];
除了“引入”這兩個(gè)變量以外,程序代碼沒(méi)有任何改變。簡(jiǎn)單地關(guān)閉register_globals選項(xiàng)促使開(kāi)發(fā)者更進(jìn)一步了解哪些數(shù)據(jù)是來(lái)自外部的(不可信任的)資源。
請(qǐng)注意這里還有一個(gè)小問(wèn)題:PHP中默認(rèn)的error_reporting設(shè)置仍然是E_ALL & ~E_NOTICE,因此如果“username”和“password”這兩個(gè)值沒(méi)有被提交,試圖從$_REQUEST數(shù)組或$_POST數(shù)組中獲得這兩個(gè)值并不會(huì)招致任何錯(cuò)誤信息。如晨不你的PHP程序需要嚴(yán)格的錯(cuò)誤檢查,你還需要增加一些代碼以首先檢查這些變量。
但是這是不是意味著更多的輸入?
是的,在象上面這樣的簡(jiǎn)單程序中,使用PHP 4.2常常會(huì)增加輸入量。但是,還是看看光明的一面吧 -- 你的程序終究是更安全了!
不過(guò)認(rèn)真的說(shuō),PHP的設(shè)計(jì)者并沒(méi)有完全忽視你的痛苦。在這些新數(shù)組中有一個(gè)特殊的其它所PHP變量都不具備的特征,它們是完全的全局變量。這對(duì)你有什么幫助呢?讓我們先對(duì)我們的示例進(jìn)行一下擴(kuò)充。
為了使得站點(diǎn)中的多個(gè)頁(yè)面可以使用用戶(hù)名/口令論證,我們將我們用戶(hù)認(rèn)證程序?qū)懙揭粋€(gè)include文件(protectme.php)中:
<?php /* protectme.php */
function authorize_user($authuser, $authpass)
{
$username = $_POST['username'];
$password = $_POST['password'];
// 檢查用戶(hù)名和口令
if ($username != $authuser or $password != $authpass):
?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php
exit();
endif;
}
?>
現(xiàn)在,我們剛才的頁(yè)面看上去將是這樣的:
<?php
require('protectme.php');
authorize_user('kevin','secret');
?>
<!-- 有安全要求的HTML內(nèi)容 -->
很簡(jiǎn)單,很清晰明了,對(duì)不對(duì)?現(xiàn)在是考驗(yàn)?zāi)愕难哿徒?jīng)驗(yàn)的時(shí)候了 -- 在authorize_user 函數(shù)中少了什么?
在函數(shù)中沒(méi)有申明$_POST是一個(gè)全局變量!在php 4.0中,當(dāng)register_globals開(kāi)啟時(shí),你需要增加一行代碼以在函數(shù)中獲取$username和$password變量:
function authorize_user($authuser, $authpass)
{
global $username, $password;
...
在PHP中,和其它具有類(lèi)似語(yǔ)法的語(yǔ)言不同,函數(shù)外的變量在函數(shù)中不能自動(dòng)獲得,你需要象上面所說(shuō)明的那樣增加一行以指定其來(lái)自global范圍。
在PHP 4.0中,當(dāng)關(guān)閉register_globals以提供安全性時(shí),你可以使用$HTTP_POST_VARS數(shù)組以獲得你的表單提交的值,但是你還是需要從全局范圍導(dǎo)入這個(gè)數(shù)組:
function authorize_user($authuser, $authpass)
{
global $HTTP_POST_VARS;
$username = $HTTP_POST_VARS['username'];
$password = $HTTP_POST_VARS['password'];
但是在PHP 4.1及以后的版本中,特殊的$_POST變量(以及上面提到的其它變量)可以在所有范圍內(nèi)使用。這就是不需要在函數(shù)中申明$_POST變量是一個(gè)全局變量的原因:
function authorize_user($authuser, $authpass)
{
$username = $_POST['username'];
$password = $_POST['password'];
這對(duì)session有什么影響?
特殊的$_SESSION數(shù)組的引入實(shí)際上有助于簡(jiǎn)化session代碼。你不需要將session變量申明為全局變量,然后再去留意哪些變量被注冊(cè)了,你現(xiàn)在可以簡(jiǎn)單地從$_SESSION['varname']中引用你所有的session變量。
現(xiàn)在讓我們來(lái)看看另一個(gè)用戶(hù)認(rèn)證的例子。這一次,我們使用sessions以標(biāo)志一個(gè)在你的網(wǎng)站繼續(xù)逗留的用戶(hù)已經(jīng)經(jīng)過(guò)了用戶(hù)認(rèn)證。首先,我們來(lái)看看PHP 4.0版本(開(kāi)啟register_globals):
<?php
session_start();
if ($username == 'kevin' and $password == 'secret')
{
$authorized = true;
session_register('authorized');
}
?>
<?php if (!$authorized): ?>
<!-- 顯示HTML表單以提示用戶(hù)登錄 -->
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
和剛開(kāi)始的程序一樣,這個(gè)程序也存在安全漏洞,在URL的最后加上?authorized=1可以繞過(guò)安全措施直接訪(fǎng)問(wèn)頁(yè)面內(nèi)容。開(kāi)發(fā)者可以將$authorized視為一個(gè)session變量而忽視了可以很容易地通過(guò)用戶(hù)輸入設(shè)置同樣的變量。
當(dāng)我們?cè)黾恿宋覀兊奶厥獾臄?shù)組(PHP 4.1)并關(guān)閉register_globals(PHP 4.2)后,我們的程序?qū)⑹沁@樣的:
<?php
session_start();
if ($username == 'kevin' and $password == 'secret')
$_SESSION['authorized'] = true;
?>
<?php if (!$_SESSION['authorized']): ?>
<!-- 顯示HTML表單以提示用戶(hù)登錄 -->
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
是不是更加簡(jiǎn)單了?你不再需要再將普通的變量注冊(cè)為一個(gè)session變量,你只需要直接設(shè)置session變量(在$_SESSION數(shù)組中),然后用同樣的方法使用它。程序變得更短了,而且對(duì)于什么變量是session變量也不會(huì)引起混亂!
總結(jié)
在這篇文章中,我解釋了PHP腳本語(yǔ)言作出改變的深層原因。在PHP 4.1中,添加了一組特殊數(shù)據(jù)以訪(fǎng)問(wèn)外部數(shù)據(jù)。這些數(shù)組可以在任何范圍內(nèi)調(diào)用,這使得外部數(shù)據(jù)的訪(fǎng)問(wèn)更方便。在PHP 4.2中,register_globals被默認(rèn)關(guān)閉以鼓勵(lì)使用這些數(shù)組以避免無(wú)經(jīng)驗(yàn)的開(kāi)發(fā)者編寫(xiě)出不安全的PHP代碼。
沒(méi)有試過(guò)我怎么知道?
這里有什么錯(cuò)誤?
看看下面的這段PHP腳本,它用來(lái)在輸入的用戶(hù)名及口令正確時(shí)授權(quán)訪(fǎng)問(wèn)一個(gè)Web頁(yè)面:
復(fù)制代碼 代碼如下:
<?php
// 檢查用戶(hù)名及口令
if ($username == 'kevin' and $password == 'secret')
$authorized = true;
?>
<?php if (!$authorized): ?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
OK,我相信大約半數(shù)的讀者會(huì)不屑的說(shuō)“太愚蠢了-- 我不會(huì)犯這樣的錯(cuò)誤的!”但是我保證有很多的讀者會(huì)想“嗨,沒(méi)什么問(wèn)題啊,我也會(huì)這么寫(xiě)的!”當(dāng)然還會(huì)有少數(shù)人會(huì)對(duì)這個(gè)問(wèn)題感到困惑(“什么是PHP?”)。PHP被設(shè)計(jì)為一個(gè)“好的而且容易的”腳本語(yǔ)言,初學(xué)者可以在很短的時(shí)間內(nèi)學(xué)會(huì)使用它;它也應(yīng)該能夠避免初學(xué)者犯上面的錯(cuò)誤。
再回到剛才的問(wèn)題,上面的代碼中存在的問(wèn)題是你可以很容易地獲得訪(fǎng)問(wèn)的權(quán)力,而不需要提供正確的用戶(hù)名和口令。只在要你的瀏覽器的地址欄的最后添加?authorized=1。因?yàn)镻HP會(huì)自動(dòng)地為每一個(gè)提交的值創(chuàng)建一個(gè)變量 -- 不論是來(lái)自動(dòng)一個(gè)提交的表單、URL查詢(xún)字符串還是一個(gè)cookie -- 這會(huì)將$authorized設(shè)置為1,這樣一個(gè)未授權(quán)的用戶(hù)也可以突破安全限制。
那么,怎么簡(jiǎn)單地解決這個(gè)問(wèn)題呢?只要在程序的開(kāi)頭將$authorized默認(rèn)設(shè)置為false。這個(gè)問(wèn)題就不存在了!$authorized是一個(gè)完全在程序代碼中創(chuàng)建的變量;但是為什么開(kāi)發(fā)者得為每一個(gè)惡意的用戶(hù)提交的變量擔(dān)心呢?
PHP 4.2作了什么改變?
在PHP 4.2中,新安裝的PHP中的register_globals選項(xiàng)默認(rèn)為關(guān)閉,因此EGPCS值(EGPCS是Environment、Get、Post、Cookies、Server的縮寫(xiě) -- 這是PHP中外部變量來(lái)源的全部范圍)不會(huì)被作為全局變量來(lái)創(chuàng)建。當(dāng)然,這個(gè)選項(xiàng)還可以通過(guò)手工來(lái)開(kāi)啟,但是PHP的開(kāi)發(fā)者推薦你將其關(guān)閉。要貫徹他們的意圖,你需要使用其它的方法來(lái)獲取這些值。
從PHP 4.1開(kāi)始,EGPCS值就可以從一組指定的數(shù)組中獲得:
$_ENV -- 包含系統(tǒng)環(huán)境變量
$_GET -- 包含查詢(xún)字符串中的變量,以及提交方法為GET的表單中的變量
$_POST -- 包含提交方式為POST的表單中的變量
$_COOKIE -- 包含所有cookie變量
$_SERVER -- 包含服務(wù)器變量,例如HTTP_USER_AGENT
$_REQUEST -- 包含$_GET、$_POST和$_COOKIE的全部?jī)?nèi)容
$_SESSION -- 包含所有已注冊(cè)的session變量
在PHP 4.1之前,當(dāng)開(kāi)發(fā)者關(guān)閉register_globals選項(xiàng)(這也被考慮為提高PHP性能的一種方法)后,必須使用諸如$HTTP_GET_VARS這樣的令人討厭的名字來(lái)獲取這些變量。這些新的變量名不僅僅短,而且它們還有其他優(yōu)點(diǎn)。
首先,讓我們?cè)赑HP 4.2中(也就是說(shuō)關(guān)閉register_globals 選項(xiàng))重寫(xiě)上面提到的代碼:
復(fù)制代碼 代碼如下:
<?php
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
// 檢查用戶(hù)名和口令
if ($username == 'kevin' and $password == 'secret')
$authorized = true;
?>
<?php if (!$authorized): ?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
正如你看到的,我所需要做的只是在代碼的開(kāi)始增加下面兩行:
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
因?yàn)槲覀兿M脩?hù)名和密碼是由用戶(hù)提交的,所以我們從$_REQUEST數(shù)組中獲取這些值。使用這個(gè)數(shù)組使得用戶(hù)可以自由選擇傳遞方式:通過(guò)URL查詢(xún)字符串(例如允許用戶(hù)創(chuàng)建書(shū)簽時(shí)自動(dòng)輸入他們的證書(shū))、通過(guò)一個(gè)提交的表單或者是通過(guò)一個(gè)cookie。如果你想要限制只能通過(guò)表單提交證書(shū)(更精確地說(shuō),是通過(guò)HTTP POST請(qǐng)求),你可以使用$_POST數(shù)組:
$username = $_POST['username'];
$password = $_POST['password'];
除了“引入”這兩個(gè)變量以外,程序代碼沒(méi)有任何改變。簡(jiǎn)單地關(guān)閉register_globals選項(xiàng)促使開(kāi)發(fā)者更進(jìn)一步了解哪些數(shù)據(jù)是來(lái)自外部的(不可信任的)資源。
請(qǐng)注意這里還有一個(gè)小問(wèn)題:PHP中默認(rèn)的error_reporting設(shè)置仍然是E_ALL & ~E_NOTICE,因此如果“username”和“password”這兩個(gè)值沒(méi)有被提交,試圖從$_REQUEST數(shù)組或$_POST數(shù)組中獲得這兩個(gè)值并不會(huì)招致任何錯(cuò)誤信息。如晨不你的PHP程序需要嚴(yán)格的錯(cuò)誤檢查,你還需要增加一些代碼以首先檢查這些變量。
但是這是不是意味著更多的輸入?
是的,在象上面這樣的簡(jiǎn)單程序中,使用PHP 4.2常常會(huì)增加輸入量。但是,還是看看光明的一面吧 -- 你的程序終究是更安全了!
不過(guò)認(rèn)真的說(shuō),PHP的設(shè)計(jì)者并沒(méi)有完全忽視你的痛苦。在這些新數(shù)組中有一個(gè)特殊的其它所PHP變量都不具備的特征,它們是完全的全局變量。這對(duì)你有什么幫助呢?讓我們先對(duì)我們的示例進(jìn)行一下擴(kuò)充。
為了使得站點(diǎn)中的多個(gè)頁(yè)面可以使用用戶(hù)名/口令論證,我們將我們用戶(hù)認(rèn)證程序?qū)懙揭粋€(gè)include文件(protectme.php)中:
復(fù)制代碼 代碼如下:
<?php /* protectme.php */
function authorize_user($authuser, $authpass)
{
$username = $_POST['username'];
$password = $_POST['password'];
// 檢查用戶(hù)名和口令
if ($username != $authuser or $password != $authpass):
?>
<!-- 未授權(quán)的用戶(hù)將在這里給予提示 -->
<p>Please enter your username and password:</p>
<form action="<?=$PHP_SELF?>" method="POST">
<p>Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" /></p>
</form>
<?php
exit();
endif;
}
?>
現(xiàn)在,我們剛才的頁(yè)面看上去將是這樣的:
復(fù)制代碼 代碼如下:
<?php
require('protectme.php');
authorize_user('kevin','secret');
?>
<!-- 有安全要求的HTML內(nèi)容 -->
很簡(jiǎn)單,很清晰明了,對(duì)不對(duì)?現(xiàn)在是考驗(yàn)?zāi)愕难哿徒?jīng)驗(yàn)的時(shí)候了 -- 在authorize_user 函數(shù)中少了什么?
在函數(shù)中沒(méi)有申明$_POST是一個(gè)全局變量!在php 4.0中,當(dāng)register_globals開(kāi)啟時(shí),你需要增加一行代碼以在函數(shù)中獲取$username和$password變量:
function authorize_user($authuser, $authpass)
{
global $username, $password;
...
在PHP中,和其它具有類(lèi)似語(yǔ)法的語(yǔ)言不同,函數(shù)外的變量在函數(shù)中不能自動(dòng)獲得,你需要象上面所說(shuō)明的那樣增加一行以指定其來(lái)自global范圍。
在PHP 4.0中,當(dāng)關(guān)閉register_globals以提供安全性時(shí),你可以使用$HTTP_POST_VARS數(shù)組以獲得你的表單提交的值,但是你還是需要從全局范圍導(dǎo)入這個(gè)數(shù)組:
function authorize_user($authuser, $authpass)
{
global $HTTP_POST_VARS;
$username = $HTTP_POST_VARS['username'];
$password = $HTTP_POST_VARS['password'];
但是在PHP 4.1及以后的版本中,特殊的$_POST變量(以及上面提到的其它變量)可以在所有范圍內(nèi)使用。這就是不需要在函數(shù)中申明$_POST變量是一個(gè)全局變量的原因:
function authorize_user($authuser, $authpass)
{
$username = $_POST['username'];
$password = $_POST['password'];
這對(duì)session有什么影響?
特殊的$_SESSION數(shù)組的引入實(shí)際上有助于簡(jiǎn)化session代碼。你不需要將session變量申明為全局變量,然后再去留意哪些變量被注冊(cè)了,你現(xiàn)在可以簡(jiǎn)單地從$_SESSION['varname']中引用你所有的session變量。
現(xiàn)在讓我們來(lái)看看另一個(gè)用戶(hù)認(rèn)證的例子。這一次,我們使用sessions以標(biāo)志一個(gè)在你的網(wǎng)站繼續(xù)逗留的用戶(hù)已經(jīng)經(jīng)過(guò)了用戶(hù)認(rèn)證。首先,我們來(lái)看看PHP 4.0版本(開(kāi)啟register_globals):
復(fù)制代碼 代碼如下:
<?php
session_start();
if ($username == 'kevin' and $password == 'secret')
{
$authorized = true;
session_register('authorized');
}
?>
<?php if (!$authorized): ?>
<!-- 顯示HTML表單以提示用戶(hù)登錄 -->
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
和剛開(kāi)始的程序一樣,這個(gè)程序也存在安全漏洞,在URL的最后加上?authorized=1可以繞過(guò)安全措施直接訪(fǎng)問(wèn)頁(yè)面內(nèi)容。開(kāi)發(fā)者可以將$authorized視為一個(gè)session變量而忽視了可以很容易地通過(guò)用戶(hù)輸入設(shè)置同樣的變量。
當(dāng)我們?cè)黾恿宋覀兊奶厥獾臄?shù)組(PHP 4.1)并關(guān)閉register_globals(PHP 4.2)后,我們的程序?qū)⑹沁@樣的:
復(fù)制代碼 代碼如下:
<?php
session_start();
if ($username == 'kevin' and $password == 'secret')
$_SESSION['authorized'] = true;
?>
<?php if (!$_SESSION['authorized']): ?>
<!-- 顯示HTML表單以提示用戶(hù)登錄 -->
<?php else: ?>
<!-- 有安全要求的HTML內(nèi)容 -->
<?php endif; ?>
是不是更加簡(jiǎn)單了?你不再需要再將普通的變量注冊(cè)為一個(gè)session變量,你只需要直接設(shè)置session變量(在$_SESSION數(shù)組中),然后用同樣的方法使用它。程序變得更短了,而且對(duì)于什么變量是session變量也不會(huì)引起混亂!
總結(jié)
在這篇文章中,我解釋了PHP腳本語(yǔ)言作出改變的深層原因。在PHP 4.1中,添加了一組特殊數(shù)據(jù)以訪(fǎng)問(wèn)外部數(shù)據(jù)。這些數(shù)組可以在任何范圍內(nèi)調(diào)用,這使得外部數(shù)據(jù)的訪(fǎng)問(wèn)更方便。在PHP 4.2中,register_globals被默認(rèn)關(guān)閉以鼓勵(lì)使用這些數(shù)組以避免無(wú)經(jīng)驗(yàn)的開(kāi)發(fā)者編寫(xiě)出不安全的PHP代碼。
沒(méi)有試過(guò)我怎么知道?
相關(guān)文章
php 多關(guān)鍵字 高亮顯示實(shí)現(xiàn)代碼
php 多關(guān)鍵字 高亮顯示實(shí)現(xiàn)代碼,需要的朋友可以參考下2012-04-04
php使用memcoder將視頻轉(zhuǎn)成mp4格式的方法
這篇文章主要介紹了php使用memcoder將視頻轉(zhuǎn)成mp4格式的方法,涉及php操作視頻文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
通達(dá)OA公共代碼 php常用檢測(cè)函數(shù)
從通達(dá)OA公共代碼扒下的php常用檢測(cè)函數(shù)代碼,學(xué)習(xí)php的朋友可以參考下。2011-12-12
php使用strpos判斷字符串中數(shù)字類(lèi)型子字符串出錯(cuò)的解決方法
這篇文章主要介紹了php使用strpos判斷字符串中數(shù)字類(lèi)型子字符串出錯(cuò)的解決方法,結(jié)合具體問(wèn)題分析了strpos函數(shù)針對(duì)數(shù)字類(lèi)型子字符串進(jìn)行判斷時(shí)的注意事項(xiàng)及類(lèi)型轉(zhuǎn)換處理技巧,需要的朋友可以參考下2017-04-04
PHP設(shè)計(jì)模式之工廠模式實(shí)例總結(jié)
這篇文章主要介紹了PHP設(shè)計(jì)模式之工廠模式,簡(jiǎn)單介紹了工廠模式的概念、原理并結(jié)合實(shí)例形式總結(jié)分析了工廠模式的具體定義及使用方法,需要的朋友可以參考下2017-09-09
php簡(jiǎn)單實(shí)現(xiàn)多語(yǔ)言切換的方法
這篇文章主要介紹了php簡(jiǎn)單實(shí)現(xiàn)多語(yǔ)言切換的方法,涉及php字符串、數(shù)組結(jié)合session操作實(shí)現(xiàn)多語(yǔ)言切換的相關(guān)技巧,需要的朋友可以參考下2016-05-05
php5 pdo新改動(dòng)加載注意事項(xiàng)
想試試pdo怎么用,把 extension=php_pdo_mssql.dll extension=php_pdo_mysql.dll2008-09-09
PHP實(shí)現(xiàn)的mysql主從數(shù)據(jù)庫(kù)狀態(tài)檢測(cè)功能示例
這篇文章主要介紹了PHP實(shí)現(xiàn)的mysql主從數(shù)據(jù)庫(kù)狀態(tài)檢測(cè)功能,結(jié)合具體實(shí)例形式分析了php檢測(cè)多個(gè)mysql主從數(shù)據(jù)庫(kù)連接狀態(tài)的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07

