《現代 PHP》學習筆記(十三):消毒、驗證及跳脫

前言

本文為《現代 PHP》一書的學習筆記。

環境

  • Windows 10
  • Wnmp 3.1.0

良好習慣

PHP 語言仍然包含了許多過時的工具,一不小心,有可能使用了這些工具做出效能差且不安全的應用程式。

所以必須要知道那些工具可以使用,哪些必須忽略。

消毒、驗證及跳脫

永遠不要相信任何來源不明的資料,一些外部的來源如下:

  • $_GET
  • $_POST
  • $_REQUEST
  • $_COOKIE
  • $argv
  • php://stdin
  • file_get_contents()
  • 遠端資料庫
  • 遠端 API
  • 來自客戶端的資料

所有上述的外部資料來源都有可能是攻擊的源頭。

消毒使用者輸入

假如網頁允許使用 HTML 下評論,在原始設定中,沒有任何東西會阻止使用者在評論中使用不合理的 <script> 標籤,例如:

1
2
3
4
<p>
This is a helpful article!
</p>
<script>window.location.href='http://example.com';</script>

如果沒有消毒這個評論,惡意的程式碼將會被注入到資料庫中。

HTML

可以利用 htmlentities() 函式,將 HTML 的特殊字元消毒成對應的 HTML 代表字元,這個函式會將字串轉化成對應用程式儲存層較安全的型態。

範例 5-1:以 htmlentities() 函式消毒使用者輸入

1
2
$input = '<p><script>alert("You won the Nigerian lottery!");</script></p>';
echo htmlentities($input, ENT_QUOTES, 'UTF-8');
  • htmlentities() 函式的第一個參數是字串、第二個參數是 ENT_QUOTES,告訴函式要將單引號進行編碼、第三個參數指定了使用者輸入的字元集。

SQL 查詢

未經處理的 SQL 查詢使得懷有惡意的人可以入侵並且破壞資料庫。

範例 5-2:不良的 SQL 查詢

1
2
3
4
5
6
$sql = sprintf(
'UPDATE users SET password = "%s" WHERE id = %s',
$_POST['password'],
$_GET['id']
);
echo $sql;

如果有人發送以下的 HTTP 請求到此 PHP 腳本,每個使用者的密碼都會被設定為 abc,因為很多 SQL 資料庫把「--」視為是註解的開頭。

Method URL Key Value
POST /user?id=1 password abc”;–

使用個人資料

PHP 提供了 filter_var()filter_input() 函式來消毒不同形式的使用者輸入,如:電子郵件位址、URL 編碼字串、整數、浮點數、HTML 字元、URL 和指定的 ASCII 字元範圍。

範例 5-3:消毒使用者輸入中的電子郵件位址

1
2
$email = 'john@example.com';
echo filter_var($email, FILTER_SANITIZE_EMAIL);

範例 5-4:消毒使用者輸入中的國際字元

1
2
3
4
5
6
7
$string = "\nIñtërnâtiônàlizætiøn\t";
$safeString = filter_var(
$string,
FILTER_SANITIZE_STRING,
FILTER_FLAG_STRIP_LOW|FILTER_FLAG_ENCODE_HIGH
);
echo $safeString;

驗證使用者輸入

與消毒不同的是,驗證並不會對使用者輸入中不合法的資料進行去除,驗證只是確認輸入資料是否達到預期。

使用 filter_var() 函式搭配任何 FILTER_VALIDATE_* 旗標來驗證使用者輸入。

範例 5-5:驗證電子郵件位址

1
2
3
4
5
6
7
$input = 'john@example.com';
$isEmail = filter_var($input, FILTER_VALIDATE_EMAIL);
if ($isEmail !== false) {
echo "Success";
} else {
echo "Fail";
}
  • filter_var() 函式如果驗證成功,會回傳被驗證的値,否則會回傳 false

不過 filter_var() 函式並沒有辦法驗證所有東西,以下元件可以補足:

跳脫使用者輸出

當要把資料輸出到網頁或是 API 響應時,跳脫輸出讓應用程式多增加一層防護。

如同前面提到的,可以用 PHP 的htmlentities() 函式跳脫輸出。

1
2
$output = '<p><script>alert("NSA backdoor installed");</script>';
echo htmlentities($output, ENT_QUOTES, 'UTF-8');

參考資料

  • Josh Lockhart(2015)。現代 PHP。台北市:碁峯資訊。