淺談PHP弱類型安全

小飛 · 2015/01/04 10:25php

0x00 弱類型初探


沒有人質疑php的簡單強大,它提供了不少特性供開發者使用,其中一個就是弱類型機制。mysql

在弱類型機制下 你可以執行這樣的操做程序員

#!php
<?php
$var = 1;
$var = array();
$var = "string";
?>
複製代碼

php不會嚴格檢驗傳入的變量類型,也能夠將變量自由的轉換類型。sql

好比 在$a == $b的比較中數組

  • $a = null; $b = false; //爲真
  • $a = ''; $b = 0; //一樣爲真

然而,php內核的開發者本來是想讓程序員藉由這種不須要聲明的體系,更加高效的開發,因此在幾乎全部內置函數以及基本結構中使用了不少鬆散的比較和轉換,防止程序中的變量由於程序員的不規範而頻繁的報錯,然而這卻帶來了安全問題。安全

0x02 知識預備 php內核之zval結構


在PHP中聲明的變量,在ZE中都是用結構體zval來保存的函數

zval的定義在zend/zend.hui

#!c
typedef struct _zval_struct zval;  

struct _zval_struct {  
    /* Variable information */  
    zvalue_value value;     /* value */  
    zend_uint refcount__gc;  
    zend_uchar type;    /* active type */  
    zend_uchar is_ref__gc;  
};  

typedef union _zvalue_value {  
    long lval;  /* long value */  
    double dval;    /* double value */  
    struct {  
        char *val;  
        int len;  
    } str;  
    HashTable *ht;  /* hash table value */  
    zend_object_value obj;  
} zvalue_value;
複製代碼

其中php經過type判斷變量類型 存入valuespa

如上也就是php內核中弱類型的封裝,也是咱們後面講的全部東西的原理和基礎。code

0x03變量的強制轉換


經過剛剛的瞭解,咱們知道zval.type決定了存儲到zval.value的類型。

當源代碼進行一些未限制類型的比較,或數學運算的時候,可能會致使zval.type的改變,同時影響zval.value的內容改變。

當int趕上string

cp.1 數學運算

當php進行一些數學計算的時候

#!php
var_dump(0 == '0'); // true
var_dump(0 == 'abcdefg'); // true  
var_dump(0 === 'abcdefg'); // false
var_dump(1 == '1abcdef'); // true 
複製代碼

當有一個對比參數是整數的時候,會把另一個參數強制轉換爲整數。

至關於對字符串部分

intval再和整數部分比較,其實也就是改變了zval.type的內容 尤其注意的是,'1assd'的轉換後的值是1,而‘asdaf’是0

也說明了intval會從第一位不是數字的單位開始進行

全部也有

#!php
var_dump(intval('3389a'));//輸出3389
複製代碼

這個例子就告訴咱們,永遠不要相信下面的代碼

#!php
if($a>1000){
    mysql_query('update ... .... set value=$a')
}
複製代碼

你覺得這時候進入該支的萬無一失爲整數了

其實$a多是1001/**/union...

cp.2 語句條件的鬆散判斷

舉個例子
php的switch使用了鬆散比較. $which會被自動intval變成0
若是每一個case裏面沒有break ,就會一直執行到包含,最終執行到咱們須要的函數,這裏是成功包含

#!php
<?php
if (isset($_GET['which']))
{
        $which = $_GET['which'];
        switch ($which)
        {
        case 0:
        case 1:
        case 2:
                require_once $which.'.php';
                break;
        default:
                echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
                break;
        }
複製代碼

cp.3 函數的鬆散判斷

#!php
var_dump(in_array("abc", $array));
複製代碼

in_array — 檢查數組中是否存在某個值 參數

needle 待搜索的值。

Note: 若是 needle 是字符串,則比較是區分大小寫的。 haystack 這個數組。

strict 若是第三個參數 strict 的值爲 TRUE 則 in_array() 函數還會檢查 needle 的類型是否和 haystack 中的相同。

能夠看到,只有加了strict纔會對類型進行嚴格比較, 那麼咱們再次把整形和字符串進行比較呢?

#!php
var_dump(in_array("abc", $array1));</br>
var_dump(in_array("1bc", $array2));
複製代碼

它遍歷了array的每一個值,而且做"=="比較(「當設置了strict 用===」)

結果很明顯了

若是array1裏面有個值爲0,那麼第一條返回就會爲真//intval('abc')=0

若是array2裏面有個值爲1,那麼第二條就會爲真//intval('1bc')=1

array_search也是同樣的原理

這裏的應用就很普遍了,

不少程序員都會檢查數組的值,

那麼咱們徹底能夠用構造好的int 0或1 騙過檢測函數,使它返回爲真

總結一下,在全部php認爲是int的地方輸入string,都會被強制轉換,好比

#!php
$a = 'asdfgh';//字符串類型的a</br>
echo $a[2];  //根據php的offset 會輸出'd'</br>
echo $a[x];  //根據php的預測,這裏應該是int型,那麼輸入string,就會被intval成爲0 也就是輸出'a'
複製代碼

當數組趕上string

這一個例子我是在德國的一個ctf中遇到,頗有意思
前面咱們講的都是string和int的比較

那麼array碰上int或者是string會有什麼化學反應?

由php手冊咱們知道

Array轉換整型int/浮點型float會返回元素個數;

轉換bool返回Array中是否有元素;轉換成string返回'Array',並拋出warning。

那麼實際應用是怎樣的呢?

#!php
if(!strcmp($c[1],$d) && $c[1]!==$d){
...
}
複製代碼

能夠發現,這個分支經過strcmp函數比較要求二者相等且「==」要求二者不相等才能進入。

strcmp() 函數比較兩個字符串。

該函數返回:

0 - 若是兩個字符串相等
<0 - 若是 string1 小於 string2
>0 - 若是 string1 大於 string2
複製代碼

這裏的strcmp函數其實是將兩個變量轉換成ascii 而後作數學減法,返回一個int的差值。

也就是說鍵入'a'和'a'進行比較獲得的結果就是0

那麼若是讓$array和‘a’比較呢?

#!php
http://localhost:8888/1.php?a[]=1
var_dump(strcmp($_GET[a],'a'));
複製代碼

這時候php返回了null!

也就是說,咱們讓這個函數出錯從而使它恆真,繞過函數的檢查。
下面給出一張鬆散比較的表格


0x04時時防備弱類型


做爲一個程序員,弱類型確實給程序員書寫代碼帶來了很大的便利,可是也讓程序員忘記了
$array =array();的習慣。
都說一切輸入都是有害的

那麼其實能夠說一切輸入的類型也是可疑的,永遠不要相信弱類型的php下任何比較函數,任何數學運算。不然,你絕對是被php出賣的那一個。

相關文章
相關標籤/搜索