使用 PHP 的 Filter 函數(過濾器)高效、安全地獲取請求參數

前言

一般,咱們獲取請求參數的方法爲直接訪問超全局變量:$_GET,$_POST,$_SERVER,$_ENV,$_COOKIE,而在 php5.2 中,內置了 filter 模塊,用於變量的驗證和過濾等操做。過濾器函數簡化了代碼結構,相對於直接訪問超全局變量來也更加的高效和安全。php

過濾器函數列表:

  • filter_has_var() — 檢測是否存在指定類型的變量。
  • filter_id() — 返回與某個特定名稱的過濾器相關聯的 id。
  • filter_input_array() — 獲取一系列外部變量,而且能夠經過過濾器處理它們。
  • filter_input() — 經過名稱獲取特定的外部變量,而且能夠經過過濾器處理它。
  • filter_list() — 返回所支持的過濾器列表。
  • filter_var_array() — 獲取多個變量而且過濾它們。
  • filter_var() — 使用特定的過濾器過濾一個變量。

使用過濾器函數檢測是否存在指定類型的變量:

bool filter_has_var(int $type ,string $variable_name);html

其中,type 包含五個常量:前端

  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_SERVER
  • INPUT_ENV

分別對應正則表達式

  • $_GET
  • $_POST
  • $_COOKIE
  • $_SERVER
  • $_ENV

這些常量在其餘過濾器函數中一樣適用。數據庫

filter_has_var() 函數在成功時返回 TRUE,或者在失敗時返回 FALSE後端

使用該函數,能夠把此段代碼安全

if (isset($_GET['name'])){
    echo htmlentities($_GET['name']);
} else {
    echo '參數不存在';
}
複製代碼

改寫爲:函數

echo filter_has_var(INPUT_GET, 'name') ? $_GET['name'] : '參數不存在';
複製代碼

可是使用此函數,只解決了判斷變量是否存在的問題,若是 $name 的值是網站

'jo<br>ne'url

那麼在 echo 的時候會出現顯示錯誤,更嚴重的是若是對方傳過來的 name 中包含了 <script> 標籤,就能夠對網站進行注入攻擊,那麼有什麼辦法來解決這個問題呢?

經過名稱獲取特定的外部變量,而且能夠經過過濾器處理它:

mixed filter_input ( int $type , string $variable_name [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

這個函數能夠說是 filter_has_var() 的增強版,它在檢測輸入是否存在的同時,還能夠傳入第三個參數(過濾器)來檢測該輸入是否符合規範,若是變量不存在返回NULL,不符合則返回 FALSE
其中第三個參數須要填一個過濾器類型(默認爲 FILTER_SANITIZE_STRING),PHP 有兩種過濾器:

  • Validating 過濾器:

    • 用於驗證用戶輸入
    • 嚴格的格式規則(好比 URL 或 E-Mail 驗證)
    • 若是成功則返回預期的類型,若是失敗則返回 FALSE
  • Sanitizing 過濾器:

    • 用於容許或禁止字符串中指定的字符
    • 無數據格式規則
    • 始終返回字符串

例:

$email_1 = filter_input(INPUT_GET,'email', FILTER_VALIDATE_EMAIL);
$email_2 = filter_input(INPUT_GET,'email', FILTER_SANITIZE_EMAIL);

echo 'VALIDATE:';
var_dump($email_1);

echo "<br>";

echo 'SANITIZE:';
var_dump($email_2);
複製代碼

當咱們使用

localhost/test.php?email=1234578<br>qq.com

訪問包含這段代碼的腳本時(顯然這是一個非法的email地址),輸出以下:

VALIDATE:bool(false)
SANITIZE:string(15) "1234578brqq.com"

當使用

localhost/test.php?email=1234578@qq.com

訪問時(合法url),輸出以下:

VALIDATE:string(14) "1234578@qq.com"
SANITIZE:string(14) "1234578@qq.com"

很明顯,當參數的值合法時,兩個過濾器會返回相同的值,可是當參數非法時,VALIDATE 會返回一個布爾值 FALSE,而 SANITIZE 返回了一個過濾掉特殊字符的字符串。

值得一提的是,能夠傳入 FILTER_VALIDATE_REGEXP 參數,從而根據 regexp,兼容 Perl 的正則表達式來驗證值:

$options = ['options'=>['default'=>'', 'regexp'=>"/^\w*$/"]];
$xxx = filter_input(INPUT_GET,'xxx', FILTER_VALIDATE_REGEXP, $options);
複製代碼

因此,VALIDATE 類型的過濾器適合用來判斷格式是否正確,而 SANITIZE 可以對給定的字符串進行過濾(具體過濾規則請參考:php.net/manual/zh/f…)。

同時咱們也發現了一個問題,雖然 SANITIZE 能夠對字符串進行過濾,可是這個過濾很是的簡單粗暴,直接把它看不順眼字符從字符串中刪掉了,這在其餘場景中或許大有用處,但在處理輸入上,一般咱們應該返回給用戶一個‘你不能這麼作!’的提示,而不該該擅自替用戶作其餘操做,或者想將那些字符轉義並保留下來,如何達到這個目的呢?

使用回調函數:FILTER_CALLBACK

若是你看系統自帶的過濾器都不順眼,那你能夠將第三個參數指定爲 FILTER_CALLBACK ,並在第四個參數的位置指定一個回調函數,來實現本身的過濾器,格式以下:

filter_input(INPUT_POST, 'email',FILTER_CALLBACK,array('options' => 'my_filter'));
複製代碼

回調函數格式以下:

function my_filter($str) {
	// 須要一個形參(本例中爲 $str)來接受字符串
	// 拿到目標字符串後就能夠對它隨心所欲了
	$str .= 'f*ck';
	// 最後記得 return 加工後的字符串
	return $str;
}
複製代碼

擴展應用

相信你也發現了,這只是 PHP 的過濾器函數中的一部分,若是你的目標字符串不是來源於前端輸入,而是後端本身加工(或是從數據庫直接取出)出來的,一樣也可使用相似的過濾器函數,如 filter_var(),該函數的語法爲:

filter_var(variable, filter, options); 能夠看到,和咱們上面使用的函數大同小異,你只須要在傳入輸入類型的地方改成傳入一個要過濾的變量就能夠了。

相關文章
相關標籤/搜索