1. 程式人生 > >PHP語言中global和$GLOBALS[]的分析 。php裡的$_REQUEST['GLOBALS']作用,以及如何處理全域性變數漏洞 。

PHP語言中global和$GLOBALS[]的分析 。php裡的$_REQUEST['GLOBALS']作用,以及如何處理全域性變數漏洞 。

PHP語言中global和$GLOBALS[]的分析 

先看下面的例子:

PHP程式碼

  1. <?php  
  2. // 例子1  
  3. function test_global() {  
  4.   global $var1$var2;  
  5.   $var2 =& $var1;  
  6. }  
  7. function test_globals() {  
  8.   $GLOBALS['var3'] =& $GLOBALS['var1'];  
  9. }  
  10. $var1 = 5;  
  11. $var2 = $var3 = 0;  
  12. test_global();  
  13. print $var2 ."\n";  
  14. test_globals();  
  15. print $var3
     ."\n";  
  16. ?>   

執行結果為: 
0
5

怎麼會這樣呢?不應該是2個5嗎?怎麼會出現1個0和1個5呢?

恩,我們保留以上問題,深入分析$GLOBALS[]和global的原理!

注意: $GLOBALS 在 PHP 3.0.0 及以後版本中適用。


我們都知道變數其實是相應實體記憶體在程式碼中的"代號"。


$GLOBALS:由所有已定義全域性變數組成的陣列,變數名就是該陣列的索引。
這是一個“superglobal”,或者可以描述為自動全域性變數。
也就是說上面程式碼中的$var1和$GLOBALS['var1']是指的同一變數,而不是2個不同的變數!

下面來分析global到底做了什麼?

我們都知道php中的函式所產生的變數都是函式的私有變數,那麼global關鍵字產生的變數也肯定逃不出這個規則,為什麼這麼說呢,看下面的程式碼:

PHP程式碼

  1. <?php  
  2. // 例子2  
  3. function test() {  
  4.   global $a;  
  5.   unset($a);  
  6. }  
  7.   
  8. $a = 1;  
  9. test();  
  10. print $a;  
  11. ?>   

複製程式碼
執行結果為: 
1
為什麼會輸出1呢?不是已經把$a給unset了嗎?unset失靈了?php的bug?

都不是,其實unset起作用了,是把test函式中的$a給unset掉了,可以在函式後面加入print $a;來測試!也就是說global產生了test函式外部$a的別名變數“$a”,為了和外面的$a區別,我把它稱為--test->$a。

接著回到上面的例子1,看test_global中的這一程式碼“$var2 =& $var1;”,上面是一個引用賦值運算,也就是$var2將指向var1所指向的實體記憶體地址。其實就是因為$var1的引用指向了$var2的引用地址。導致實質的值沒有改變。這時候只是指向$var1的指標指向了$var2的指標,只是指標指向變了一下,但是實質上根本就沒有改變$var2的值,因此$var2的值仍舊不會變化)


所以我們得出一個結論,在函式中global和$GLOBALS[]的區別在於:

global在函式中產生一個指向函式外部變數的別名變數,而不是真正的函式外部變數,一但改變了別名變數的指向地址,就會發生一些意料不到情況,例如例子1.

$GLOBALS[]確確實實呼叫是外部的變數,函式內外會始終保持一致!

===============================================================================

php裡的$_REQUEST['GLOBALS']作用,以及如何處理全域性變數漏洞 

眾所周知,當php.ini裡面的register_globals=on時,各種變數都被注入程式碼,例如來自 HTML 表單的請求變數。再加上 PHP 在使用變數之前是無需進行初始化的。那麼就有可能導致不安全,假如有人惡意發出這麼一個get請求"http://yourdomain/unsafe.php?GLOBALS=",那麼就會清除$GLOBALS變數的值而導致不安全。所以我們可以這樣子寫

[php]
if ((isset($_REQUEST['GLOBALS']) 
       OR isset($_FILES['GLOBALS'])) 
       AND ini_get('register_globals')) { 
        die(globals overwrite attempted.');
}

[/php]

 

========================

register_globals是php.ini裡的一個配置,這個配置影響到php如何接收傳遞過來的引數,如果你的問題是:為什麼我的表單無法傳遞資料?為什麼我的程式無法得到傳遞過來的變數?等等,那麼你需要仔細的閱讀以下的內容。 

register_globals的值可以設定為:On或者Off,我們舉一段程式碼來分別描述它們的不同。 
 

程式碼:

<form name="frmTest" id="frmTest" action="URL"> 
<input type="text" name="user_name" id="user_name"> 
<input type="password" name="user_pass" id="user_pass"> 
<input type="submit" value="login"> 
</form> 



當register_globals=Off的時候,下一個程式接收的時候應該用$_GET['user_name']和$_GET['user_pass']來接受傳遞過來的值。(注:當<form>的method屬性為post的時候應該用$_POST['user_name']和$_POST['user_pass']) 

當register_globals=On的時候,下一個程式可以直接使用$user_name和$user_pass來接受值。 

顧名思義,register_globals的意思就是註冊為全域性變數,所以當On的時候,傳遞過來的值會被直接的註冊為全域性變數直接使用,而Off的時候,我們需要到特定的數組裡去得到它。所以,碰到上邊那些無法得到值的問題的朋友應該首先檢查一下你的register_globals的設定和你獲取值的方法是否匹配。(檢視可以用phpinfo()函式或者直接檢視php.ini) 

那我們為什麼要使用Off呢?原因有2: 
1、php以後的新版本預設都用Off,雖然你可以設定它為On,但是當你無法控制伺服器的時候,你的程式碼的相容性就成為一個大問題,所以,你最好從現在就開始用Off的風格開始程式設計 
2、這裡有兩篇文章介紹為什麼要Off而不用On 
http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=php3&Number=292803&page=0&view=collapsed&sb=5&o=all&fpart= 
http://www.php.net/manual/en/security.registerglobals.php 

現在還有一個問題就是,以前用On風格寫的大量指令碼怎麼辦? 
如果你以前的指令碼規劃得好,有個公共包含檔案,比如config.inc.php一類的檔案,在這個檔案里加上以下的程式碼來模擬一下(這個程式碼不保證100%可以解決你的問題,因為我沒有大量測試,但是我覺得效果不錯)。另外,這個帖子裡的解決方法也可以參考一下(http://www.chinaunix.net/forum/viewtopic.php?t=159284)。 
 

程式碼:

<?php 
if ( !ini_get('register_globals') ) 

    extract($_POST); 
    extract($_GET); 
    extract($_SERVER); 
    extract($_FILES); 
    extract($_ENV); 
    extract($_COOKIE); 
    
    if ( isset($_SESSION) ) 
    { 
        extract($_SESSION); 
    } 

?> 

register_globals = Off的情況不僅僅影響到如何獲取從<form>、url傳遞過來的資料,也影響到session、cookie,對應的,得到session、cookie的方式應該為:$_SESSION[]、$_COOKIE。同時對於session的處理也有一些改變,比如,session_register()沒有必要而且失效,具體的變化,請檢視php manual裡的Session handling functions

$_REQUEST中間的內容實際上還是來源於$_GET $_POST $_COOKIE,缺點是無法判斷變數到底來自於get post 還是cookie,對要求比較嚴格的場合不適用。