in_array的坑

PHP in_array的坑

ps: 應該是弱類型語言的坑

php文檔

顧名思義,in_array就是查找一個值是否在數組裏面。

問題

事故現場

一個sql注入的測試代碼如下:

$type = $_GET['type'];
$types = [2,3,4,5,6];
if(!in_array($type, $types)) {
	throw new ParamsException('參數錯誤');
}

$sql = sprintf('select * from test where `type` = %s', $type);

$result = $db->fetch($sql);

emm,讓我們理一下。

  1. 從參數獲取type
  2. 檢驗type合法性
  3. 拼sql
  4. 查找數據

好像是這麼回事。

但是,線上報警系統說,這個接口有sql注入風險。

wait,我知道你們會說,這是沒有用過預處理,肯定會注入。但是看官,且聽我慢慢道來,這個項目是一個及其遠古的項目,若是要改動的話,成本和收益不成正比,領導也不會同意大改。而且,嗯,我們的參數只有一個,而且,我們校驗了參數的合法性。爲什麼會被注入呢?

問題查找

既然我們的參數只有一個,而且我們確認已經過濾了,那麼仍然存在能被注入的風險,就證明了,我們的校驗不對。

ok,我們先了解下什麼是常見的sql注入。

所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。具體來說,它是利用現有應用程序,將(惡意的)SQL命令注入到後臺數據庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。 [1] 比如先前的很多影視網站泄露VIP會員密碼大多就是通過WEB表單遞交查詢字符暴出的,這類表單特別容易受到SQL注入式攻擊.

舉個栗子:

select * from a where id = 10; 正常sql

select * from a where id = 10 or 1; 注入sql

換到我們的場景而言,就是 type = 3 變成了 type = 3 or 1,那豈不是把所有值都返回了。

嗯???不是校驗了type嗎?怎麼會這樣注入。

好,實踐是檢驗真理的唯一標準。

$arr = [3,4,5];
$str = '3 or 1';
var_dump(in_array($str, $arr));

打印出來bool(true)!!!!

想必都驚呆了,原來是in_array校驗數據出了問題。

再進行我的猜測。

$str = '3 or 1';
$int = intval($str);
echo $int; //3
var_dump($a == $int); // bool(true)

soga,原來,檢測是這麼容易通過的。我推斷是因爲在進行值比價的時候,因爲沒有使用強類型驗證,所以,會將 3 or 1 == 3,從而判定是是在數組裏。

然後繼續仔細查看文檔,發現in_array有第三個參數,作用是強類型判斷是否相等(嚴格校驗)。但是我想,還是不適合我的場景,我的場景是允許字符串傳入的。

解決問題

居然問題找到了,解決就自然有簡單了。

解決方案還是不說了,因爲不同場景各位看官要用的解決方式也不一樣。

思路基本是一樣的,就是保證不會被異常數據注入(我這選擇的是根據參數返回已經寫好的where,哈,項目求穩)。

源碼關注

github

PHP_FUNCTION(in_array)
{
	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

ps: array_search最終調用的也是php_search_array

	zval *value,				/* value to check for */
		 *array,				/* array to check in */
		 *entry;				/* pointer to array entry */
	zend_ulong num_idx;
	zend_string *str_idx;
	zend_bool strict = 0;		/* strict comparison or not */

	ZEND_PARSE_PARAMETERS_START(2, 3)
		Z_PARAM_ZVAL(value)
		Z_PARAM_ARRAY(array)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(strict)
	ZEND_PARSE_PARAMETERS_END();

	if (strict) {
		//強類型驗證......
		//這裏用的fast_is_identical_function(value, entry))
	} else {
	   //可以觀察到,php是先檢測數組的值得類型,而不是根據查找值得類型進行比較
		if (Z_TYPE_P(value) == IS_LONG) {
		//long驗證,在php裏是整形
		//if (fast_equal_check_long(value, entry)) {
		} else if (Z_TYPE_P(value) == IS_STRING) {
			
		}//...其他類型

而 fast_equal_check_string的原型如下

static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2)
{
	zval result;
	if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
		return Z_LVAL_P(op1) == Z_LVAL_P(op2);
	}
	compare_function(&result, op1, op2);
	return Z_LVAL(result) == 0;
}

可以看到,當2個參數類型不一樣的時候,會走compare_function

compare_function是zend Api內核提供的,源碼暫時無法追蹤,但是根據官方的說明,結果和預測的一樣。

具體請查看官方:php對比2個值是否相等--比較運算符

後記

不要脫離了框架就沒法寫代碼

不要只說新標準,通常都要維護很多老代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章