2019 PHP安全指南

2019 年,大多數(shù)的科技工作者 — 尤其是 Web 開發(fā)者 — 必須擯棄掉關(guān)于開發(fā)安全 PHP 應(yīng)用的老一套。這對那些不相信能夠開發(fā)出安全的 PHP 應(yīng)用的人來說尤其重要.

image

這篇指南應(yīng)該作為 PHP: The Right Way 這本電子書強調(diào)安全部分的補充,而不是作為代碼風格一樣的普通主題.

PHP 版本

長話短說::除非你沒有辦法, 2018 年你在最好使用 PHP 7.2 , 同時在 2019 年早些時候計劃切換到 7.3。

PHP 7.2 已經(jīng)于 2017 年 11 月 30 號發(fā)布.

在撰寫本文時,只有 PHP 7.1 和 7.2 會得到了 PHP 語言開發(fā)人員的積極支持,而 PHP 5.6 和 7.0 只會在大約一年的時間內(nèi)獲得安全補丁。

雖然一些操作系統(tǒng)為不受支持的 PHP 版本提供長期支持,但是這種做法普遍被認為是有害的。特別在于,他們會在不升級版本號的情況下提供安全補丁,這個壞習慣會使得僅通過 PHP 版本來判斷系統(tǒng)的安全性變得非常困難。

綜上,無論其他供應(yīng)商做出什么樣的承諾,只要你能夠做到,都應(yīng)該在任何時間內(nèi)只運行獲得積極支的 PHP 版本。這樣,即使使用了一段時間的安全版本,持續(xù)不斷的升級工作也會讓你的生活免于不愉快的意外。

依賴管理

簡而言之:使用 Composer .

Composer 是對于 PHP 生態(tài)系統(tǒng)最先進的依賴管理方案, 我們強烈推薦的PHP: The Right Way里有專門一整節(jié)用于如何開始使用 Composer .

如果你不使用 Composer 來管理你的依賴項,你最終會 (希望延遲但可能事情很快發(fā)生) 出現(xiàn)這樣一種情況,你所依賴的庫變得過時,舊版本的漏洞被黑客利用.

重點: 始終記得在開發(fā)軟件時更新依賴項 . 幸運的是,這是一條命令:

composer update

您還需要 PECL.

推薦的包

無論您正在構(gòu)建什么,您幾乎肯定會從這些依賴項中獲益。這是大多數(shù) PHP 開發(fā)人員推薦的(比如:PHPUnit、PHP-CS-fixer)。

roave/security-advisories

Roave 的安全報告 使用了名叫 Friends of PHP 的包來保證你的項目不會依賴任何已知的非健壯的包。

composer require roave/security-advisories:dev-master

或者,你可以 上傳你的 composer.lock 文件到 Sensio Labs 作為常規(guī)自動漏洞評估工作的一部分,它會把所有使用的過期的包告知你。

vimeo/psalm

Psalm 是一個靜態(tài)分析工具,可以幫助識別你代碼中的 bug。雖然還有其他很好的靜態(tài)分析工具(例如: Phan and PHPStan ,它們都很好),如果您發(fā)現(xiàn)自己需要支持 php 5,沒問題,psalm 是支持 php 5.4 + 的。

Psalm 是很容易上手的:

# 目前還沒有Version 1 ,但是讓我們拭目以待:
composer require --dev vimeo/psalm:^0# 
僅需這樣操作:
vendor/bin/psalm --init# 
一如慣例的操作:
vendor/bin/psalm

果你是第一次在現(xiàn)有的代碼庫上運行,可能會看到很多紅色提示。這是正常的,除非你創(chuàng)造了一個如同 Wordpress 一樣強大的應(yīng)用程序,否則不太可能使全部測試通過以滿足符合 Herculean 的要求。

無論您使用哪種靜態(tài)分析工具,我們建議您將運用到持續(xù)集成工作流(如果適用),以便在每次代碼更改后運行它。

HTTPS 和瀏覽器安全

簡而言之: HTTPS, which should be tested, and security headers.

在 2018 年,通過不安全的 HTTP 訪問網(wǎng)站將不再被接受。幸運的是,得益于 ACME 協(xié)議和 Let's Encrypt certificate authority 的存在,我們可以免費獲取 TLS 證書并自動更新它們。

將 Acme 集成到 Web 服務(wù)器中簡直就是小菜一碟。

  • Caddy: 自動生成.

  • Apache: 不久將以 mod_-md. 的形式提供, 這里有高質(zhì)量的教程 在互聯(lián)網(wǎng)上提供。

  • Nginx: 相對簡單.

你也許會想,“好吧,我有一個 TLS 證書。現(xiàn)在,我必須花幾個小時來處理配置,然后才能保證它的安全和快速?!?/p>

Nope! Mozilla 支持. 您可以使用配置生成器根據(jù)預(yù)期的訪問群體構(gòu)建推薦的密碼套件

HTTPS (HTTP over TLS) 是絕對不可協(xié)商如果您希望您的網(wǎng)站是安全的。使用 HTTPS 可以立即消除對用戶的幾類攻擊(中間建注入、竊聽、重復(fù)攻擊和多種形式的會話操作,否則將允許用戶模擬)。

安全 Headers

雖然將 HTTPS 連接到服務(wù)器上確實為用戶增加了安全性和性能優(yōu)勢,但你可以通過利用其他瀏覽器安全功能更進一步的擴大這些優(yōu)勢。其中大多數(shù)都涉及在內(nèi)容里發(fā)送 HTTP 響應(yīng)頭。

  • Content-Security-Policy

  • 你需要這個 header,因為它可以對瀏覽器允許加載的內(nèi)部和外部資源進行精度控制,從而為跨站點腳本漏洞提供強大的防御層。

  • 請參閱 CSP-Builder,以便快速輕松地部署 / 管理內(nèi)容安全策略。

  • 要進行更深入的分析,Scott Helme 的 Content-Security-Policy 標題簡介是一個很好的起點。

  • Expect-CT

  • 你需要這個 header,因為它通過強制將不良居心的人的證書的證據(jù)發(fā)布到可公開驗證的數(shù)據(jù)結(jié)構(gòu),從而為惡意 / 受損的證書頒發(fā)機構(gòu)添加了一層保護。 了解更多關(guān)于 “Expect-CT” 的信息

  • 首先將這個 header 設(shè)置為 enforce,max-age = 30,并增加 max-age,如此操作讓服務(wù)更穩(wěn)定。

  • Referrer-Policy

  • 你需要這個 header,因為它允許您控制是否將有關(guān)用戶行為的信息泄露給第三方。

  • 再次深入了解 Scott Helme 深入探討Referrer-Policy headers

  • 將 header 設(shè)置為 “same-origin” 或 “no-referrer”,除非您有理由允許更寬松的設(shè)置。

  • Strict-Transport-Security

  • 你需要這個 header,因為它告訴瀏覽器通過 HTTPS 強制所有將來的請求到同一個來源而不是不安全的 HTTP。

  • 首次部署時將其設(shè)置為 “max-age = 30”,然后當您確信沒有任何內(nèi)容會中斷時,將此值增加到某個較大的值(例如 “31536000”)。

  • X-Content-Type-Options

  • 你需要這個 header,因為 MIME 類型混淆可能導(dǎo)致不可預(yù)測的結(jié)果,包括允許 XSS 漏洞的奇怪邊緣情況。最好伴隨標準的 Content-Type 標題。

  • 設(shè)置為 nosniff,除非您需要默認行為(例如,用于文件下載)。

  • X-Frame-Options

  • 你需要這個 header,因為它可以防止點擊劫持。

  • 設(shè)置為 DENY(或 SAMEORIGIN,但僅當你使用 <frame> 元素時)

  • X-XSS-Protection

    • 你需要這個 header,因為它啟用了默認情況下未啟用的某些瀏覽器 Anti-XSS 功能。
    • 設(shè)為 1; mode=block

同樣,如果你使用 PHP 的內(nèi)置會話管理功能(推薦使用),你可能想要調(diào)用 session_start(),如下所示:

session_start([
    'cookie_httponly' => true,
    'cookie_secure' => true]);

這會強制你的應(yīng)用在發(fā)送會話標識符 cookie 時使用僅 HTTP 和安全標記,阻止成功的 XSS 攻擊竊取用戶的 cookie 并強制它們分別僅通過 HTTPS 發(fā)送。我們之前已經(jīng)在 2015 年的博文中介紹了安全 PHP 會話

子資源一致性攻擊(Subresource Integrity)

在將來的某個時候,你可能會使用 CDN 將常用的 Javascript / CSS 框架和庫放到一個集中的地方。

負責安全的工程師對這種操作表示有很明顯的安全隱患:那就是,許多網(wǎng)站使用 CDN 來提供內(nèi)容,那么攻擊 CDN 并替換內(nèi)容注入這些代碼到成千上萬數(shù)千的網(wǎng)站。

輸入 subresource integrity.

Subresource integrity (SRI) 允許你固定你希望 CDN 提供的文件內(nèi)容的哈希值。當前實現(xiàn)的 SRI 僅允許使用安全加密散列函數(shù),這意味著攻擊者生成與原始文件產(chǎn)生相同散列的相同資源的惡意版本是不可行的。

真實的案例: Bootstrap v4-alpha 在他們的 CDN 示例片段中使用 SRI.

<link
    rel="stylesheet"
    
    integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
    crossorigin="anonymous"/><script
    src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"
    integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
    crossorigin="anonymous"></script>

文檔關(guān)聯(lián)

Web 開發(fā)人員經(jīng)常在超鏈接上設(shè)置 target 屬性(例如:target="_blank")實現(xiàn)在新窗口中打開鏈接。但是,如果您沒有傳遞 ' rel="noopener" 標記,則可能允許目標頁面控制原始頁面。

不要這樣做

<a  target="_blank">Click here</a>

這讓example.com 可以控制當前的網(wǎng)頁。

替換成這樣

<a  target="_blank" rel="noopener noreferrer">Click here</a>

這將在一個新窗口中打開 example.com,但不會將當前窗口的控制權(quán)交給可能懷有惡意的第三方。

延伸閱讀。

開發(fā)安全的 PHP 軟件

如果應(yīng)用程序安全性是一個新主題,請從應(yīng)用安全性詳解開始.

許多安全領(lǐng)域的專家都會向開發(fā)人員提供諸如 OWASP Top 10 之類的資源.

但是,大多數(shù)常見漏洞可以歸類為同一類的安全問題(代碼 / 數(shù)據(jù)沒有完全分離,邏輯不健全,操作環(huán)境不安全或加密協(xié)議缺陷)。

給新手們說明安全問題是一個很簡單的,容易實現(xiàn)的事情,但是如何解決這些安全問題將是更長遠的安全工程。

因此, 我們只推薦類前 10 名或前 25 名安全檢查清單.

數(shù)據(jù)庫交互

深入了解: PHP 防止 SQL 注入 https://paragonie.com/blog/2015/05/preventing-sql-injection-in-php-applications-easy-and-definitive-guide

如果您自己編寫 SQL 查詢,請確保您使用的是預(yù)備表達式,并且將網(wǎng)絡(luò)或文件系統(tǒng)提供的任何信息都作為參數(shù)傳遞,而不是拼接查詢字符串。此外,確保你沒有使用模擬的預(yù)備表達式.

為達到最佳效果,使用 EasyDB.

*** 不要這樣做:**

/* 不安全代碼: 
*/$query = $pdo->query("SELECT * FROM users WHERE username = '" . $_GET['username'] . "'");

替換成這樣:

/* 防止SQL注入: */
$results = $easydb->row("SELECT * FROM users WHERE username = ?", $_GET['username']);

還有其他一些數(shù)據(jù)庫抽象層提供了同等的安全性(EasyDB 實際上在底層使用 PDO,但是為了防止安全隱患,它特意禁用準備好的語句模擬,而使用實際的準備好的語句)。只要用戶輸入不能影響查詢的結(jié)構(gòu),您就是安全的。(這包括存儲過程。)

文件上傳

深入了解: 如何安全地允許用戶上傳文件 https://paragonie.com/blog/2015/10/how-securely-allow-users-upload-files

接受文件上傳是一個冒險的主張,但只要采取一些基本的預(yù)防措施,就可以安全地執(zhí)行此操作。也就是說,應(yīng)當阻止意外地允許執(zhí)行或解釋上傳文件的方式直接訪問上傳文件.

上傳的文件應(yīng)該是只讀的或讀寫的,永遠不能被執(zhí)行.

如果你的站點根目錄是 /var/www/example.com, 你不應(yīng)該存貯上傳文件在 /var/www/example.com/uploaded_files.

相反,你應(yīng)該將上傳文件存儲在無法通過瀏覽器直接訪問的單獨目錄中 (例如, /var/www/example.com-uploaded/), 以免它們意外地作為服務(wù)器端腳本執(zhí)行,打開遠程代碼執(zhí)行的漏洞。

更簡單的解決方案是將站點根目錄向下移動一級 (例如,移動到:/var/www/example.com/public)。

文件上傳的另一個問題是安全地 下載文件 。

直接訪問 SVG 圖像將在用戶的瀏覽器中執(zhí)行 JavaScript 代碼。 這是真的,盡管 SVG 圖像的 MIME 類型是以image\為前綴.

MIME 類型嗅探可能導(dǎo)致類型混淆攻擊。詳情見前文安全 HeadersX-Content-Type-Options 響應(yīng)頭的介紹。

如果你不采納前面關(guān)于如何安全存貯上傳文件的建議,攻擊者可能設(shè)法上傳.php 或者.phtml的文件, 通過直接在瀏覽器中訪問上傳文件執(zhí)行任意代碼,從而獲得對服務(wù)器的完全控制。請安全地玩你的服務(wù)器。

跨站腳本 (XSS)

深入閱讀: 關(guān)于防止 PHP 中的跨站點腳本漏洞,您需要了解的所有內(nèi)容 https://paragonie.com/blog/2015/06/preventing-xss-vulnerabilities-in-php-everything-you-need-know

在理想的情況下, XSS 和 SQL 注入一樣容易預(yù)防。我們有簡單易用的 API ,用于將查詢語句和數(shù)據(jù)進行分離。

不幸的是,大多數(shù) web 開發(fā)人員的實際工作會涉及到將生成的一長串 HTML 文本并通過 HTTP 協(xié)議響應(yīng)。這不是 PHP 獨有的功能,它只是 web 開發(fā)的基本工作方式。

減少 XSS 漏洞并不是那么難做。但是,瀏覽器安全 的所有內(nèi)容是至關(guān)重要。簡而言之:

  1. 經(jīng)常對輸出轉(zhuǎn)義,而不是對輸入轉(zhuǎn)義**。 如果你在數(shù)據(jù)庫中存儲已過濾的數(shù)據(jù),然后在一些地方發(fā)現(xiàn) SQL 注入漏洞,那么攻擊者可以通過惡意代碼篡改可信的數(shù)據(jù)記錄,從而完全繞過 XSS 保護。

  2. 如果你的框架中有一個提供自動上下文過濾的模板引擎,那可以使用它。它將會給你的框架的功能的安全性提供保障。

  3. echo htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); 是一種安全有效的方式,它可以阻止對 UTF-8 編碼的 web 頁面的 XSS 攻擊,但它不允許任何 HTML 標簽內(nèi)容輸出。

  4. 如果你的需求是允許你使用 Markdown 替代 HTML,不要使用 HTML 。

  5. 如果你需要允許一些 HTML 并且沒有使用模板引擎(請參見 #1),那么就使用 HTML 凈化器。 HTML 凈化器不適用于將內(nèi)容轉(zhuǎn)義成 HTML 標簽屬性的場景。

  6. 對于用戶提供的 URLs ,你應(yīng)該只允許使用 http:https: 協(xié)議模式;不要用 javascript: 。此外,使用 URL 編碼方式編碼用戶的所有輸入。

跨站請求偽造 (CSRF)

跨站請求偽造是一種混亂的代理攻擊,通過這種攻擊,你可以利用用戶的權(quán)限,欺騙用戶的瀏覽器執(zhí)行惡意的 HTTP 請求來實現(xiàn)攻擊者的目的。

這在一般情況下可通過兩個簡單步驟解決:

  1. 使用 HTTPS,這是前置條件。沒有 HTTPS,任何防御都會變得脆弱,然而單純的 HTTPS 并不能阻止 CSRF。

  2. 添加基本的請求 - 響應(yīng)的身份驗證。

  • 在所有表單中添加一個隱藏的表單值。

  • 用安全的隨機加密字符串來填充這個值(稱為令牌)。

  • 驗證表單中是否含有這個隱藏的值,并且校驗是否與設(shè)置的一致。

我們編寫了一個名為 Anti-CSRF 的庫來更進一步了解如何防范 CSRF。

  • 為了防止重復(fù)攻擊,每個令牌只能使用一次。

  • 多個令牌都存儲在后端。

  • 令牌輪換一旦達到上限,優(yōu)先使用最老的。

  • 每個令牌都可以綁定到指定的資源地址(URI)。

  • 一旦令牌泄露,那么它也不能用于其他資源請求。

  • 令牌可以選擇綁定到指定的 IP 地址。

  • 從 v2.1 版本開始,令牌可以重復(fù)使用(即 AJAX 調(diào)用)。

如果你沒有使用一個能夠幫你解決 CSRF 的框架,那么 Anti-CSRF 能夠幫到你。

在將來,使用 cookie 的 SameSite 屬性能夠更加簡單地防范 CSRF。

譯者注:截至 2016 年 4 月,Chrome 51、Opera 39、火狐 60 已實現(xiàn)了 cookie 的 Same-Site 屬性。

XML 攻擊 (XML 外部實體注入,XPath 注入)

對于那些過多依賴 XML 數(shù)據(jù)結(jié)構(gòu)來處理繁重的業(yè)務(wù)邏輯的程序而言,這里有兩個比較容易被攻擊的點。

  1. XML 外部實體注入 (XXE)XPath 注入

  2. XXE 攻擊會利用服務(wù)器執(zhí)行本地或遠端的的惡意外部文件, 或者執(zhí)行其他惡意的行為.

在早期版本的谷歌安全文檔中就有顯著的提及 XXE 攻擊,但對于大多數(shù)的大量執(zhí)行 XML 操作的非商業(yè)應(yīng)用來說對 XXE 的防范意識是薄弱的。

其實最簡單的減輕 XXE 攻擊影響的方式就是:

libxml_disable_entity_loader(true);

XPath 注入 和 SQL 注入的原理一樣,只不過是把攻擊對象換成了 XML 文檔。

比較幸運的是,PHP 對 XPath 操作方法的查詢參數(shù)是很比較特殊和形式固定的。

另一方面呢,PHP 對于 XPath 注入沒有提供簡單有效的防御手段(參數(shù)過濾)。

對于 XPath 查詢參數(shù)過濾,你的最佳方案就是使用白名單策略匹配過濾。

<?phpdeclare(strict_types=1);class SafeXPathEscaper{
    /**
     * @param string $input
     * @return string
     */
    public static function allowAlphaNumeric(string $input): string    {
        return \preg_replace('#[^A-Za-z0-9]#', '', $input);
    }
    /**
     * @param string $input
     * @return string
     */
    public static function allowNumeric(string $input): string    {
        return \preg_replace('#[^0-9]#', '', $input);
    }}// 用法示例:$selected = $xml->xpath(
    "/user/username/" . SafeXPathEscaper::allowAlphaNumeric(
        $_GET['username']
    ));

這里我們采用白名單的策略比黑名單策略安全。

反序列化和 PHP 對象注入

深度閱讀: php 反序列化安全的實現(xiàn) https://paragonie.com/blog/2016/04/securely-implementing-de-serialization-in-php

如果你將不可信的數(shù)據(jù)傳遞給 unserialize(),你將不得不面對以下兩個窘境:

  1. PHP 對象注入風險,一種利用 POP 鏈(POP chain)來觸發(fā)其他誤用對象的漏洞。php 反序列化 POP 鏈的構(gòu)造與理解異常語法

  2. 帶來的 PHP 解析器本身的內(nèi)存奔潰。

對于大多數(shù)開發(fā)者來講他們更傾向于用 JSON 來替代對對象的序列化操作,這樣的話確實規(guī)避了很多安全風險,但又引發(fā)了另外一個安全問題不得不提:DoS 攻擊 - Hash 碰撞攻擊( json_decode () 容易遭受 DoS 攻擊 - Hash 碰撞攻擊 (Hash-DoS) )。 遺憾的是, PHP 的 Hash-DOS 問題還沒有得到徹底解決。

對于 Hash-DOS 利用的 php Hash 沖突的問題。從 djb33 遷移到 Siphash,對于字符串輸入,哈希輸出的最高位設(shè)置為 1 ,對于整數(shù)輸入設(shè)置為 0 ,使用CSPRNG提供的請求密鑰,將完全解決這些攻擊。

遺憾的是, PHP 團隊還沒有準備好放棄他們已經(jīng)在 PHP 7 系列中取得的性能提升,所以很難說服他們放棄 djb33 (這是非常快但不安全的) 采用 SipHash (這也是快速的,但不像 djb33 那么快,但更安全)。 如果性能受到重大影響,可能會阻礙未來版本的采用,但也影響了安全性。

因此我們當前能做的是:

  • 使用 JSON 的方式,因為它比unserialize()更安全。

  • 在任何可能的地方,在反序列化之前確保輸入是經(jīng)過安全過濾的。

  • 對于提供給用戶的數(shù)據(jù),通過一個只有服務(wù)器知道的秘鑰使用 sodium_crypto_auth()sodium_crypto_auth_verify() 驗證。

  • 對于第三方提供的數(shù)據(jù),讓他們使用 sodium_crypto_sign()簽名他們的 JSON 消息,然后使用sodium_crypto_sign_open() 和第三方公鑰驗證消息。

  • 如果你需要對傳輸?shù)暮灻M行十六進制或 Base64 位編碼,也可以使用分離的簽名 API 。

  • 如果你無法確保 參數(shù)中 JSON 字符串的安全性,請嚴格限制單個 IP 訪問頻率以避免高頻的攻擊。(laravel 框架可以采用 Throttle 配置)

密碼哈希

深入了解: 在 2016 年如何安全存儲用戶密碼 https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016

密碼安全的存儲曾經(jīng)是一個備受爭議的話題,但現(xiàn)在實現(xiàn)起來相當簡單,尤其是在 PHP 中:

$hash = \password_hash($password, PASSWORD_DEFAULT);if (\password_verify($password, $hash)) {
    // 已驗證
    if (\password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        // 刷新,更新數(shù)據(jù)庫
    }}

您甚至不需要知道后臺使用的是什么算法,因為如果您使用的是最新版本的 PHP,那么您也將使用最新的最新版本,并且一旦有新的默認算法可用,用戶的密碼將自動升級。

不管你做什么, 在 WordPress 不要這樣做.
如果你對此感到好奇:從 php 5.5 到 7.2,默認算法是 bcrypt。將來,它可能會切換到 Argon2,即 [密碼散列類型]https://password-hashing.net/).

如果您以前沒有在 API 中使用加密,同時需要遷移舊散列,請使用這種方法. 很多公司在這方面做了錯誤的操作,其中典型就的公司有,雅虎。, Yahoo. 最近,錯誤地實現(xiàn)舊哈希升級似乎已經(jīng)導(dǎo)致了蘋果最近的 iamroot bug caused Apple's recent iamroot bug.

一般用途的加密

這是我們之前已經(jīng)詳細討論過的話題:

通常,你總希望使用 Sodium 加密庫 (libsodium)對應(yīng)用層加密。如果需要低于 7.2 版本(直到 5.2.4)你可以使用 sodium_compat 并假設(shè)你的用戶也在使用 7.2 。

在特定的實例中,由于嚴格的算法選擇和互操作性,你可能需要一個不同的庫。如果有疑問,可以向加密專家咨詢加密方式,并向密碼工程師咨詢這種實現(xiàn)是否安全。(這就是我們提供的服務(wù)之一 。)

如果你直接使用密碼 / 模式, 請參考關(guān)于加密最佳實踐的簡要指南。

隨機數(shù)

深入了解: 如何在 PHP 中安全地生成隨機字符串和整數(shù) https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php

如果您需要隨機數(shù),請使用 random_int(). 如果需要隨機字節(jié)字符串,請使用 random_bytes(). 所以不要使用 mt_rand(), rand(), 或uniqid() .

如果你需要從一個密鑰生成偽隨機數(shù),不要用 srand() 或者 mt_srand(), 請查看 SeedSpring .

<?php
use ParagonIE\SeedSpring\SeedSpring;
$seed = random_bytes(16);$rng = new SeedSpring($seed);
$data = $rng->getBytes(1024);
$int = $rng->getInt(1, 100);

服務(wù)器端 HTTPS 請求

簡而言之:確保沒有禁用 TLS 證書驗證。

請隨意使用你熟悉且兼容 PSR-7 規(guī)范的 HTTP 客戶端,大多數(shù)人喜歡使用 Guzzle 庫。也有一些人喜歡直接使用 cURL。

無論你用哪一種,你可以 使用 Certainty 庫來確保你擁有最新的證書包 , 這進而允許你啟用最嚴格的 TLS 證書驗證設(shè)置,并保護服務(wù)器的出站 HTTPS 請求。

安裝 Certainty 庫非常簡單:

composer require paragonie/certainty:^1

使用 Certainty 庫也很容易:

<?php
use ParagonIE\Certainty\RemoteFetch;
$latestCACertBundle = (new RemoteFetch())->getLatestBundle();
# 使用 cURL:
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_CAINFO, $latestCACertBundle->getFilePath());
# 使用 Guzzle:
    /** @var \GuzzleHttp\Client $http */
    $repsonse = $http->get(
        'https://example.com', 
        [
            'verify' => $latestCACertBundle->getFilePath()
        ]

這將保護你免受 Web 服務(wù)器與你集成的任何第三方 API 之間的中間人攻擊 (man-in-the-middle attacks)。

我們真的 需要 Certainty 庫嗎?

嚴格來說,為了保護您的系統(tǒng),Certainty 庫不是必要的。缺少它并不是弱點。

但是沒有 Certainty 庫。 開源軟件必須推測操作系統(tǒng)的 CACert 包是否存在,并且如果它推測錯誤,它會經(jīng)常失敗得很慘并導(dǎo)致可用性問題。

縱觀歷史,這激勵了許多開發(fā)人員禁用證書驗證,這樣他們的代碼就可以 “正常工作” 了,卻沒有意識到他們的應(yīng)用程序在受到主動攻擊時是多么不堪一擊。

Certainty 庫通過使 CACert 捆綁包位于最新的和可預(yù)測的位置來消除這種激勵。Certainty 庫還為許多希望運行他們自己內(nèi)部 CA 的企業(yè)提供了許多工具。

誰來禁用證書驗證?

常見的內(nèi)容管理系統(tǒng)(WordPress,Magento 等)的插件 / 擴展開發(fā)人員可以做到! 這是我們在生態(tài)系統(tǒng)層面試圖解決的一個巨大問題。 它不是孤立于任何特定的 CMS,你會發(fā)現(xiàn)插件等。 因為所有這些都是不安全的。

如果您正在使用這樣的 CMS,請在插件中搜索 CURLOPT_SSL_VERIFYPEERCURLOPT_SSL_VERIFYHOST ,您可能會發(fā)現(xiàn)有幾個將這些值設(shè)置為 FALSE

應(yīng)該避免的事情

不要使用 mcrypt,一個已經(jīng)十多年沒有開發(fā)的密碼學庫。如果您正在關(guān)注 我們的 PHP 版本推薦,自從 PHP 7.2 及更新版本中沒有提供 mcrypt,這應(yīng)該是一個容易避開的陷阱。

配置驅(qū)動安全建議應(yīng)該有大部分被忽視了。如果你正在閱讀 PHP 安全指南并且他們告訴你修改 php.ini 配置,而不是編寫更優(yōu)秀的代碼,那么你可能正在閱讀非常過時的建議。 關(guān)掉當前窗口,并轉(zhuǎn)移到那些不吹噓 register_globals 的頁面中。

不要使用 JOSE (JWT, JWS, JWE),盡管腳注表示已經(jīng)寫進了標準。但這是一套編纂成一系列容易出錯的密碼學設(shè)計,因為某些原因吸引了大量追崇者而形成的互聯(lián)網(wǎng)標準 。

加密 URL 參數(shù)是一種很多公司用來混淆元數(shù)據(jù)采用的反面模式 (e.g. 我們有多少使用者呢?)。在產(chǎn)生安全感的錯覺同時,它攜帶著實現(xiàn)錯誤的高風險。我們在相關(guān)文章中提出了一個更安全的替代方案。

除非你真的迫不得已,否則不要實現(xiàn) "忘記密碼" 功能。直言不諱地講: 密碼重置功能就是一個后門。 有一些方法可以實現(xiàn)它們,這些方法對合理的威脅模型是安全的,但是這應(yīng)該也給了高風險用戶一個讓他們完全選擇退出的機會。

如果可以,避免使用 RSA, 而使用 libsodium。如果你一定要 RSA,確保你指定了 OAEP 進行填補。

<?php
openssl_private_decrypt(
    $ciphertext,
    $decrypted, // 明文在成功時寫入這個變量。
    $privateKey,
    OPENSSL_PKCS1_OAEP_PADDING // 重要:不要忽略這個!
);

如果你專注于使用 PKCS#1 v1.5 進行填補,無論你集成了什么,幾乎都會很容易被 機器人攻擊,所以將其作為一個允許明文公開和偽造簽名的漏洞報告給合適的供應(yīng)商 (或 US-CERT)。

專業(yè)使用案例

既然你已經(jīng)掌握了在 2018 年及以后構(gòu)建安全 PHP 應(yīng)用程序的基礎(chǔ)知識,現(xiàn)在讓我們來看一些更專業(yè)的使用案例。

可搜索的加密

深入閱讀:使用 PHP 和 SQL 建立可搜索的加密數(shù)據(jù)庫 https://paragonie.com/blog/2017/05/building-searchable-encrypted-databases-with-php-and-sql

可搜索的加密數(shù)據(jù)庫是可取的,但實現(xiàn)它被普遍認為不是非常重要。上面的博文試圖讓讀者更深入地了解我們的解決方案,但實際上你只需:

設(shè)計您的架構(gòu),使數(shù)據(jù)庫即使泄露也不會讓攻擊者獲取到您的加密密鑰。

使用密鑰加密你的數(shù)據(jù)。

基于 HMAC 或帶有靜態(tài)鹽的安全 KDF(例如:Argon2)創(chuàng)建多個索引(使用它們自己獨特的密鑰)。

可選:截斷步驟 3 的輸出,將其作為一個布隆過濾器(Bloom filter)。

在你的 SELECT 查詢中使用第 3 或第 4 步的輸出結(jié)果。

解密結(jié)果。

在這個過程中的任何步驟,你都可以根據(jù)你的實際情況來做調(diào)整。

無邊信道的基于令牌的身份認證

深入閱讀: 拆分令牌:無邊信道的基于令牌的身份驗證協(xié)議 https://paragonie.com/blog/2017/02/split-tokens-token-based-authentication-protocols-without-side-channels

說到數(shù)據(jù)庫(上一章節(jié)),你知道 SELECT 查詢理論上可以成為定時信息泄漏的來源嗎?

簡單的防范措施:

切分你的認證令牌。

一半在 SELECT 查詢中使用。

在一定的時間內(nèi)使用后半部分進行驗。

您可以選擇將后半部分的哈希存儲在數(shù)據(jù)庫中,而不是它本身。這對于只使用一次的令牌是有意義的,如用在 “密碼重置” 或 “在這臺計算機上記住我” 等地方的令牌。

這樣即使你可能會因為定時泄漏被別人竊取到一半的令牌,剩下的一半也需要暴力破解才能成功。

開發(fā)安全的 API

深入閱讀: 使用 Sapient 讓 PHP 開發(fā)的 API 更為堅固 https://paragonie.com/blog/2017/06/hardening-your-php-powered-apis-with-sapient

我們編寫了 SAPIENT(the Secure API ENgineering Toolkit)使服務(wù)器到服務(wù)器之間傳遞身份驗證信息變得更為簡單。

除了具有 HTTPS 提供的安全性之外,Sapient 還允許你使用共享密鑰或公鑰來加密和驗證信息。

即使有一個用惡意 / 受損的證書頒發(fā)機構(gòu)武裝自己的中間人攻擊(man-in-the-middle)攻擊者,也能讓你可以對 API 請求進行身份驗證 ,并使用 ED25519 或只能由接收服務(wù)器的密鑰解密的加密信息對目標服務(wù)器響應(yīng)。

由于每個 HTTP 消息體都通過安全加密進行了身份驗證,因此可以安全地使用它來代替有狀態(tài)令牌可能會被篡改的協(xié)議(例如 OAuth)。但當涉及到密碼學時,在做任何非標準的事情之前,都應(yīng)該始終確保專家對它們的實現(xiàn)進行了研究。

Sapient 所使用的所有密碼學算法都由 Sodium cryptography library 提供。

拓展閱讀:

Sapient 文檔

Sapient 教程

Sapient 規(guī)范

Paragon Initiative Enterprises 已經(jīng)在其許多產(chǎn)品(包括許多開源軟件項目)中使用了 Sapient,并將繼續(xù)將軟件項目添加到 Sapient 產(chǎn)品組合中。

使用 Chronicle 記錄安全事件日志

深入閱讀: Chronicle 會讓你質(zhì)疑區(qū)塊鏈技術(shù)的需求 https://paragonie.com/blog/2017/07/chronicle-will-make-you-question-need-for-blockchain-technology

Chronicle 是一個基于哈希鏈(hash-chain)數(shù)據(jù)結(jié)構(gòu)的僅追加的加密分類賬(append-only cryptographic ledger),它具有許多吸引公司使用的 “區(qū)塊鏈” 技術(shù)的屬性,也不會有太多多余的功能。

除了僅追加加密分類賬這樣具有創(chuàng)造性的使用案例之外,當集成到 SIEM 中時,Chronicle 也是非常有亮點的,因為你可以將安全關(guān)鍵事件發(fā)送到私有的 Chronicle,使它們不可被改變。

如果你設(shè)置 Chronicle 將其摘要哈希交叉簽名到其他 Chronicle 實例,或者配置了其他實例來復(fù)制 Chronicle 的內(nèi)容,那么攻擊者就很難篡改你的安全事件日志了。

使用 Chronicle,您可以獲得區(qū)塊鏈具有的所有彈性(resilience)特點,而不會出現(xiàn)任何隱私、性能和可擴展性問題。

要將數(shù)據(jù)發(fā)布到本地 Chronicle,您可以使用任何與 Sapient 兼容的 API,最簡單的方式是 Quill。

作者的一些話

很多聰明的讀者應(yīng)該注意到了我們參考了很多我們自己的工作內(nèi)容(包括了一些之前發(fā)的博文和我們的開源項目),但其實我們并不僅僅參考我們自己的內(nèi)容。

這并不意外。

本公司自從 2015 成立以來一直致力于編寫安全庫并參與改善 PHP 生態(tài)系統(tǒng)安全性。

我們已經(jīng)涉及了很多領(lǐng)域,但我們的安全工程師(就在最近發(fā)布的 PHP 7.2 中,他們推動了 PHP 核心更安全的加密技術(shù)的發(fā)展)自己并不太擅長炒作,也不會拘泥于過去所做的工作。很可能您甚至沒有聽說過我們多年來開發(fā)的一個半個工具或庫。對此我們深感抱歉。

然而,我們也不可能成為所有方面的先驅(qū),所以我們會盡可能地與一些符合公共利益而不是自私自利的專家們公事。這就是為什么在瀏覽器安全性的部分引用了 Scott Helme 和他的公司的工作內(nèi)容,他們在使開發(fā)人員能夠接觸和理解這些新的安全特性方面做了大量的工作。

本篇指南也不是完全詳盡的。寫不安全的代碼的方法幾乎和寫安全的代碼的方法一樣多。安全不僅僅是目的,更是一種心態(tài)。有了上面所寫的內(nèi)容,以及接下來的資源,我們希望這能幫助全世界的開發(fā)人員從今天起用 PHP 編寫安全的軟件。

資源

如果您已經(jīng)閱讀的本篇的所有內(nèi)容,并且還想了解更多的知識,那么您可能會對我們針對學習應(yīng)用程序安全而制定的閱讀列表感興趣。

如果您覺得您寫的代碼足夠安全,并且希望我們以安全工程師的角度對其進行評價的話,那么來找我們吧,這實際上就是我們?yōu)榭蛻籼峁┑囊豁?a target="_blank">服務(wù)之一。

如果您所在的公司將要進行規(guī)范測試(如 PCI-DSS, ISO 27001 等),那么您可能會想要雇用我的公司來審核您的源代碼。與其他安全咨詢機構(gòu)相比,我們的流程對開發(fā)人員更加友好。

以下就是 PHP 和信息安全社區(qū)提供的資源列表了,這些資源有助于你們以自己的方式提高網(wǎng)絡(luò)安全性。

PHP 之道,真正的現(xiàn)代 PHP 開發(fā)指南。

Mozilla 的 SSL 配置生成器

Let's Encrypt,這是一個免費提供 TLS 證書的證書頒發(fā)機構(gòu)網(wǎng)站,他們正在盡自己最大的努力來創(chuàng)建更安全的網(wǎng)絡(luò)環(huán)境。

Qualys SSL 庫 為 TLS 配置提供了一個快速簡單的測試套件。實際上,每個人都用它來解決他們的密碼套件和證書問題,原因:它能很好地完成這項工作。

Security Headers 讓您檢驗?zāi)木W(wǎng)站在利用瀏覽器安全功能保護用戶方面的表現(xiàn)好壞。

Report-URI 是一個很棒的免費資源,能幫助您快速的主動啟用一個安全頭(kickstarting initiatives to implement security headers)。他們會為您提供一個 Report-URI 傳遞給您的用戶的瀏覽器,如果有什么異常發(fā)生或發(fā)現(xiàn) XSS 攻擊媒介,它們就會向 Report-URI 發(fā)送報告。 Report-URI 會收集這些錯誤信息,讓您更好地分類和排除錯誤。

The PHP Security Advent Calendar ,RIPSTech 團隊制作。

Snuffleupagus,一個以安全為導(dǎo)向的 PHP 模塊(繼承自 Suhosin 的思想,似乎大部分已經(jīng)被棄用)。

PHP Delusions,一個致力于更好地使用 PHP 的網(wǎng)站。作者的很多語調(diào)都非常固執(zhí)己見,但作者對技術(shù)準確性和清晰度的奉獻精神還是使其值得一讀的,尤其是對于那些不太熟悉 PDO 許多特性的人來說。

Have I Been Pwned? 幫助用戶發(fā)現(xiàn)他們的數(shù)據(jù)是否在過去發(fā)生數(shù)據(jù)泄露。

本文參考地址:https://www.php.cn/toutiao-421971.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容