鍍金池/ 教程/ PHP/ 安全
依賴管理
安全
測試
使用模板
開發(fā)實踐
入門指南
服務(wù)器與部署
社區(qū)
語言亮點
錯誤與異常
虛擬化技術(shù)
資源
文檔撰寫
數(shù)據(jù)庫
依賴注入
緩存
代碼風(fēng)格指南

安全

Web 應(yīng)用程序安全

攻擊者無時無刻不在準(zhǔn)備對你的 Web 應(yīng)用程序進行攻擊,因此提高你的 Web 應(yīng)用程序的安全性是非常有必要的。幸運的是,來自開放式 Web 應(yīng)用程序安全項目 (OWASP) 的有心人已經(jīng)整理了一份包含了已知安全問題和防御方式的全面的清單。這份清單對于具有安全意識的開發(fā)者來說是必讀的。

密碼哈希

每個人在建構(gòu) PHP 應(yīng)用時終究都會加入用戶登錄的模塊。用戶的帳號及密碼會被儲存在數(shù)據(jù)庫中,在登錄時用來驗證用戶。

在存儲密碼前正確的哈希密碼是非常重要的。哈希密碼是單向不可逆的,該哈希值是一段固定長度的字符串且無法逆向推算出原始密碼。這就代表你可以哈希另一串密碼,來比較兩者是否是同一個密碼,但又無需知道原始的密碼。如果你不將密碼哈希,那么當(dāng)未授權(quán)的第三者進入你的數(shù)據(jù)庫時,所有用戶的帳號資料將會一覽無遺。有些用戶可能(很不幸的)在別的網(wǎng)站也使用相同的密碼。所以務(wù)必要重視數(shù)據(jù)安全的問題。

使用 password_hash 來哈希密碼

password_hash 函數(shù)在 PHP 5.5 時被引入。 此函數(shù)現(xiàn)在使用的是目前 PHP 所支持的最強大的加密算法 BCrypt 。 當(dāng)然,此函數(shù)未來會支持更多的加密算法。 password_compat 庫的出現(xiàn)是為了提供對 PHP >= 5.3.7 版本的支持。

在下面例子中,我們哈希一個字符串,然后和新的哈希值對比。因為我們使用的兩個字符串是不同的('secret-password' 與 'bad-password'),所以登錄失敗。

{% highlight php %}
<?php
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    // Correct Password
} else {
    // Wrong password
}
{% endhighlight %}

數(shù)據(jù)過濾

永遠不要信任外部輸入。請在使用外部輸入前進行過濾和驗證。filter_var()filter_input() 函數(shù)可以過濾文本并對格式進行校驗(例如 email 地址)。

外部輸入可以是任何東西:$_GET$_POST 等表單輸入數(shù)據(jù),$_SERVER 超全局變量中的某些值,還有通過 fopen('php://input', 'r') 得到的 HTTP 請求體。記住,外部輸入的定義并不局限于用戶通過表單提交的數(shù)據(jù)。上傳和下載的文檔,session 值,cookie 數(shù)據(jù),還有來自第三方 web 服務(wù)的數(shù)據(jù),這些都是外服輸入。

雖然外部輸入可以被存儲、組合并在以后繼續(xù)使用,但它依舊是外部輸入。每次你處理、輸出、連結(jié)或在代碼中包含時,請?zhí)嵝炎约簷z查數(shù)據(jù)是否已經(jīng)安全地完成了過濾。

數(shù)據(jù)可以根據(jù)不同的目的進行不同的 過濾 。比如,當(dāng)原始的外部輸入被傳入到了 HTML 頁面的輸出當(dāng)中,它可以在你的站點上執(zhí)行 HTML 和 JavaScript 腳本!這屬于跨站腳本攻擊(XSS),是一種很有殺傷力的攻擊方式。一種避免 XSS 攻擊的方法是在輸出到頁面前對所有用戶生成的數(shù)據(jù)進行清理,使用 strip_tags() 函數(shù)來去除 HTML 標(biāo)簽或者使用 htmlentities() 或是 htmlspecialchars() 函數(shù)來對特殊字符分別進行轉(zhuǎn)義從而得到各自的 HTML 實體。

另一個例子是傳入能夠在命令行中執(zhí)行的選項。這是非常危險的(同時也是一個不好的做法),但是你可以使用自帶的 escapeshellarg() 函數(shù)來過濾執(zhí)行命令的參數(shù)。

最后的一個例子是接受外部輸入來從文件系統(tǒng)中加載文件。這可以通過將文件名修改為文件路徑來進行利用。你需要過濾掉"/", "../", null 字符或者其他文件路徑的字符來確保不會去加載隱藏、私有或者敏感的文件。

數(shù)據(jù)清理

數(shù)據(jù)清理是指刪除(或轉(zhuǎn)義)外部輸入中的非法和不安全的字符。

例如,你需要在將外部輸入包含在 HTML 中或者插入到原始的 SQL 請求之前對它進行過濾。當(dāng)你使用 PDO 中的限制參數(shù)功能時,它會自動為你完成過濾的工作。

有些時候你可能需要允許一些安全的 HTML 標(biāo)簽輸入進來并被包含在輸出的 HTML 頁面中,但這實現(xiàn)起來并不容易。盡管有一些像 HTML Purifier 的白名單類庫為了這個原因而出現(xiàn),實際上更多的人通過使用其他更加嚴(yán)格的格式限制方式例如使用 Markdown 或 BBCode 來避免出現(xiàn)問題。

查看 Sanitization Filters

有效性驗證

驗證是來確保外部輸入的是你所想要的內(nèi)容。比如,你也許需要在處理注冊申請時驗證 email 地址、手機號碼或者年齡等信息的有效性。

查看 Validation Filters

配置文件

當(dāng)你在為你的應(yīng)用程序創(chuàng)建配置文件時,最好的選擇時參照以下的做法:

  • 推薦你將你的配置信息存儲在無法被直接讀取和上傳的位置上。
  • 如果你一定要存儲配置文件在根目錄下,那么請使用 .php 的擴展名來進行命名。這將可以確保即使腳本被直接訪問到,它也不會被以明文的形式輸出出來。
  • 配置文件中的信息需要被針對性的保護起來,對其進行加密或者設(shè)置訪問權(quán)限。

注冊全局變量

注意: 自 PHP 5.4.0 開始,register_globals 選項已經(jīng)被移除并不再使用。這是在提醒你如果你正在升級舊的應(yīng)用程序的話,你需要注意這一點。

當(dāng) register_globals 選項被開啟時,它會使許多類型的變量(包括 $_POST, $_GET$_REQUEST)被注冊為全局變量。這將很容易使你的程序無法有效地判斷數(shù)據(jù)的來源并導(dǎo)致安全問題。

例如:$_GET['foo'] 可以通過 $foo 被訪問到,也就是可以對未聲明的變量進行覆蓋。如果你使用低于 5.4.0 版本的 PHP 的話,請 確保 register_globals 是被設(shè)為 off 的。

錯誤報告

錯誤日志對于發(fā)現(xiàn)程序中的錯誤是非常有幫助的,但是有些時候它也會將應(yīng)用程序的結(jié)構(gòu)暴露給外部。為了有效的保護你的應(yīng)用程序不受到由此而引發(fā)的問題。你需要將在你的服務(wù)器上使用開發(fā)和生產(chǎn)(線上)兩套不同的配置。

開發(fā)環(huán)境

為了在開發(fā)環(huán)境中顯示所有可能的錯誤,將你的 php.ini 進行如下配置:

{% highlight ini %}
display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On
{% endhighlight %}

將值設(shè)為 -1 將會顯示出所有的錯誤,甚至包括在未來的 PHP 版本中新增加的類型和參數(shù)。 和 PHP 5.4 起開始使用的 E_ALL 是相同的。- php.net

E_STRICT 類型的錯誤是在 5.3.0 中被引入的,并沒有被包含在 E_ALL 中。然而從 5.4.0 開始,它被包含在了 E_ALL 中。這意味著什么?這表示如果你想要在 5.3 中顯示所有的錯誤信息,你需要使用 -1 或者 E_ALL | E_STRICT。

不同 PHP 版本下開啟全部錯誤顯示

  • < 5.3 -1E_ALL
  •   5.3 -1E_ALL | E_STRICT
  • > 5.3 -1E_ALL

生產(chǎn)環(huán)境

為了在生產(chǎn)環(huán)境中隱藏錯誤顯示,將你的 php.ini 進行如下配置:

{% highlight ini %}
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On
{% endhighlight %}

當(dāng)在生產(chǎn)環(huán)境中使用這個配置時,錯誤信息依舊會被照常存儲在 web 服務(wù)器的錯誤日志中,唯一不同的是將不再顯示給用戶。更多關(guān)于設(shè)置的信息,請參考 PHP 手冊:

上一篇:開發(fā)實踐下一篇:依賴管理