沒有人質疑php的簡單強大,它提供了很多特性供開發者使用,其中一個就是弱類型機制。
在弱類型機制下 你能夠執行這樣的操作
#!php
<?php
$var = 1;
$var = array();
$var = "string";
?>
php不會嚴格檢驗傳入的變量類型,也可以將變量自由的轉換類型。
比如 在$a == $b的比較中
然而,php內核的開發者原本是想讓程序員借由這種不需要聲明的體系,更加高效的開發,所以在幾乎所有內置函數以及基本結構中使用了很多松散的比較和轉換,防止程序中的變量因為程序員的不規范而頻繁的報錯,然而這卻帶來了安全問題。
在PHP中聲明的變量,在ZE中都是用結構體zval來保存的
zval的定義在zend/zend.h
#!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判斷變量類型 存入value
如上也就是php內核中弱類型的封裝,也是我們后面講的所有東西的原理和基礎。
通過剛剛的了解,我們知道zval.type決定了存儲到zval.value的類型。
當源代碼進行一些未限制類型的比較,或數學運算的時候,可能會導致zval.type的改變,同時影響zval.value的內容改變。
當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...
舉個例子 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;
}
#!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'
這一個例子我是在德國的一個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!
也就是說,我們讓這個函數出錯從而使它恒真,繞過函數的檢查。 下面給出一張松散比較的表格
作為一個程序員,弱類型確實給程序員書寫代碼帶來了很大的便利,但是也讓程序員忘記了 $array =array();的習慣。 都說一切輸入都是有害的
那么其實可以說一切輸入的類型也是可疑的,永遠不要相信弱類型的php下任何比較函數,任何數學運算。否則,你絕對是被php出賣的那一個。