PHP應用程式安全設計指北

前言

2018 年將至,一般程序員(特別是 Web 開發程序員)應當拋棄過去開發PHP程序的很多不好的習慣和觀念了。雖然部分人不以為意,但是這確實是事實。

這個指南應該以重點部分作為 PHP: The Right Way 安全章節的補充,而不是以一般的 PHP 編程話題。

正文

PHP 版本

請在 2018 年使用 PHP 7.2, 並且計劃 2019 年初切換到 PHP 7.3。

PHP 7.2 已於 2017 年 11 月 30 日發佈。

寫這篇文章的時候,只有 7.1 和 7.2 版本還在被 PHP 官方積極維護,而 5.6 和 7.0 只在大概1年內提供安全補丁更新。

對於其他官方不維護的 PHP 版本,雖然某些操作系統會提供長期支持和維護,但這其實通常是有害的。尤其是他們提供安全支持補丁卻沒有版本號,這使得很難解釋系統的安全性(僅僅知道 PHP 版本)。

因此,無論其他供應商提出了什麼承諾,如果可以,你就應該在任何時候都堅決地使用官方提供支持的 PHP 版本。這樣,儘管最終是一個短暫的安全版本,但一個不斷致力於升級的版本,總會讓你收穫一些意外的驚喜。

依賴管理

人生苦短,我用 Composer

在 PHP 生態中,Composer 是最先進的依賴管理方案。我們推薦 PHP: The Right Way 中關於依賴管理的完整章節。

如果你沒有使用 Composer 來管理應用的依賴,最終(hopefully later but most likely sooner)會導致應用裡某個依賴會嚴重過時,然後老舊版本中的漏洞會被利用於計算機犯罪。

重要: 開發軟件時,時常記得保持依賴的更新。幸運地,這隻需一行命令:

composer update

如果你正在使用某些專業的,需要使用 PHP 擴展(C 語言編寫),那你不能使用 Composer 管理,而需要 PECL 。

推薦擴展

不管你正在編寫什麼,你總會受益於這些依賴。這是除了大多數 PHP 程序員的推薦(PHPUnit, PHP-CS-Fixer, ...)外的補充。

roave/security-advisories

Roave's security-advisories 使用 Friends of PHP repository 確保你的項目沒有依賴一些已知易受攻擊的依賴。

composer require roave/security-advisories:dev-master 

或者,你可以上傳你的composer.lock文件到 Sensio Labs ,作為例行自動化漏洞評估工作流的一部分,以提醒發現任何過時的軟件包。

vimeo/psalm

Psalm 是一個幫助你識別代碼裡可能存在 bugs 的靜態分析工具。還有其他很好的靜態分析工具(例如 Phan 和 PHPStan 都很棒),但當你發現你需要支持 PHP 5,Psalm 將是 PHP 5.4+ 的首選。

使用 Psalm 挺簡單:

# Version 1 doesn't exist yet, but it will one day:
composer require --dev vimeo/psalm:^0
# Only do this once:
vendor/bin/psalm --init
# Do this as often as you need:
vendor/bin/psalm

如果你是第一次在現有代碼庫運行,可能會看到很多紅色錯誤。但除非你在構建像 WordPress 那麼大的程序,否則努力通過所有測試絕不是艱鉅的。

無論使用哪種靜態分析工具,我們都推薦你能將他加入到持續集成工作流(Continuous Integration workflow)中,以便在每次更改代碼中運行。

HTTPS 和瀏覽器安全

HTTPS, which should be tested, and security headers .

2018 年,不安全的 HTTP 網站將不再被接受。幸運的是,由於 ACME 協議 和 Let's Encrypt certificate authority,免費的 TLS 證書成為了可能。

將 ACME 集成到你的服務器,小菜一碟。

  • Caddy: 自動加入。
  • Apache: 很快作為mod_md可用。在此之前,網上很多高質量教程。
  • Nginx: 相對簡單。

你也許會想,“好,我已經有 TLS 證書了,為了網站變得安全和快速,得花些時間折騰配置信息。”

不!Mozilla做了件好事情!。你可以根據網站的目標受眾,使用配置生成器生成推薦套件。

如果你希望網站安全,HTTPS ( HTTP over TLS ) 是絕對不能妥協的。使用 HTTPS 立刻就能消除多種攻擊(中間人攻擊、竊聽、重放攻擊以及若干允許用戶模仿的會話形式的攻擊)。

安全頭

在服務器使用 HTTPS 確實為用戶提供了許多安全性和性能方面的好處,但也還能通過利用某些瀏覽器的安全功能來進一步提升安全性。而這大部分會涉及到響應內容的安全頭。

  • Content-Security-Policy你需要該 Header ,因為它提供了對於瀏覽器是否允許加載內部和外部資源的細化控制,從而為跨域腳本攻擊漏洞提供了有效防禦層。
  • 參閱 CSP-Builder,以便快速簡便地部署/管理內容安全策略(Content Security Policies)。
  • 為了更加深入的分析, Scott Helme's introduction to Content-Security-Policy headers,會是一個很好的引導。
  • Expect-CT你需要該 Header ,因為它能通過強制某些不良行為者將其錯誤證書的證據頒發到可公開驗證的僅可追加的數據結構,從而針對流氓/受損的證書頒發機構增加一層防護。
  • 優先設置為enforce,max-age=30。只要你有足夠的自信該 Header 不會造成服務中斷,增加max-age吧。
  • Referrer-Policy你需要該 Header ,因為它允許你控制用戶的行為信息是否洩露給第三方。
  • 同樣地,Scott Helme 提供了一篇關於Referrer-Policy Header 介紹好文。
  • 除非有理由允許更加寬鬆的設置,否則請設置為same-origin或no-referrer。
  • Strict-Transport-Security你需要該 Header ,因為它告訴瀏覽器通過 HTTPS 而不是不安全的 HTTP ,將 future requests 設為同源。
  • 在第一次部署時,將其設置為max-age = 30,然後當你確信沒有任何內容會中斷時,將此值增加到某個較大的值(例如 31536000)。
  • X-Content-Type-Options你需要該 Header ,因為 MIME 類型的混淆可能會導致不可預知的結果,包括奇怪的允許 XSS 漏洞的邊緣情況。這最好伴隨著一個標準的 Content-Type Header 。
  • 除非需要默認的行為(例如文件的下載),否則請設置為nosniff。
  • X-Frame-Options你需要該 Header ,因為它允許你防止點擊劫持。
  • 設置為DENY (或者SAMEORIGIN, 但僅僅當你使用元素的時候)。
  • X-XSS-Protection你需要該 Header ,因為它啟用了一些默認情況下未啟用的瀏覽器反 XSS 功能。
  • 設置為1; mode=block。

同樣,如果你使用 PHP 的內置會話管理功能(建議使用),則可能需要這樣調用session_start():

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

這會強制你的應用在發送會話標識符時使用 HTTP-Only 和 Secure 標誌,從而防止 XSS 攻擊竊取用戶的 Cookie ,並強制它們分別通過 HTTPS 發送。 我們之前在 2015 年的博客文章中介紹了安全的 PHP 會話。

子資源完整性

在將來的某個時候,你也許會使用 CDN 來加載網站的公共 JavaScript/CSS 庫。安全工程師已經遇見了這存在一個明顯的風險,如果很多網站使用 CDN 提供內容,Hack 和替換 CDN(獲得了 CDN 的控制權)就可以注入(惡意)代碼到成千上萬的網站。

查閱子資源完整性吧。

子資源完整性(SRI,Subresource integrity)允許你將希望 CDN 服務的文件的內容進行哈希處理。目前實行的 SRI 只允許使用安全的密碼散列函數,這意味著攻擊者不可能生成與原始文件哈希相同的惡意版本資源。

一個真實例子: Bootstrap v4-alpha uses SRI in their CDN example snippet

 rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
crossorigin="anonymous"
/>

文檔關係

Web 開發人員經常在超鏈接上設置目標屬性(例如,target ="_ blank"在新窗口中打開鏈接)。但是,如果你沒有傳遞rel ="noopener"標籤,則可以允許目標頁面控制當前頁面。

不要這樣做:


這會讓http://example.com頁面能控制當前頁面。

而應該這樣做:


通過這樣在新窗口打開https://example.com,當前窗口的控制權也不會授予可能的惡意第三方。

可以更加深入研究。

開發安全的 PHP 程序

如果應用程序安全性對你來說是一個新話題,請從應用程序安全性簡介開始吧。

大多數安全專家指出,開發者可以使用 OWASP Top 10 等資源開始著手。

但是,大多數常見的漏洞也可以是相同高等級的安全問題(例如代碼和數據沒有完全分離、邏輯不嚴謹和健全、操作環境不安全或是可破譯的密碼協議等)。

我們的假設是,應該授予安全新手知道一些更簡單、基礎的安全知識和問題,並如何解決這些問題,應該是一個更好的、長遠的安全工程。

因此,我們避免推薦十大或二十大安全清單。

數據庫注入

避免 PHP 程序存在 SQL 注入。

如果你是自己編寫 SQL 代碼,請確保使用prepared語句,並且從網絡或文件系統提供的信息都作為參數傳遞,而不是字符串拼接的形式。此外,確保你沒有使用模擬的prepared語句。

為了達到好的效果,可以使用 EasyDB 。

不要這樣做:

/* Insecure code: */
$query = $pdo->query("SELECT * FROM users WHERE username = '" . $_GET['username'] . "'");

應該這樣做:

/* Secure against SQL injection: */
$results = $easydb->row("SELECT * FROM users WHERE username = ?", $_GET['username']);

還有其他數據庫抽象層提供了相同的安全性(EasyDB實際上是在使用 PDO ,但在實際的prepare語句前避免了prepared語句模擬)。 只要用戶輸入不會影響查詢的結構,就很安全(包括存儲過程)。

文件上傳

深入:如何安全地允許用戶上傳文件?

接受文件上傳是一個冒險的提議,但只要採取一些基本的預防措施,是能保證安全的。也就是說,允許文件直接上傳的話,這些文件可能會被意外的允許執行或解釋。上傳的文件應該是隻讀(read-only)或讀寫(read-write)的,永遠不應該可執行(executable)。

如果你的網站根目錄是/var/www/example.com,請不要保存上傳文件在/var/www/example.com/uploaded_files。

而應該保存到一個不能直接訪問的目錄(例如:/var/www/example.com-uploaded/),以免意外地將其作為服務器端腳本執行,並獲得執行遠程代碼的後門。

一個更加簡潔的方法是將網站根目錄往下移動一個層級(即:/var/www/example.com/public)。

如何安全地下載這些上傳文件也是一個問題。

  • 直接訪問 SVG 圖像類型時,將在用戶瀏覽器執行 JavaScript 代碼。儘管它的MIME類型中的image/前綴具有誤導性,但是這是正確的。
  • 正如前面提及的,MIME 類型嗅探可能導致類型混淆攻擊。請參閱X-Content-Type-Options。
  • 如果你放棄前面關於如何安全地存儲上傳文件的建議,攻擊者就會通過上傳 .php 或 .phtml 文件,直接在瀏覽器中訪問文件來執行任意代碼,從而完全控制服務器。

跨站腳本

關於 PHP 中的跨站腳本攻擊,你想知道的都在這裡

同樣地,預防 XSS 和 SQL 注入是一樣簡單的。我們有簡單而易用的 API 來分離文檔結構(structure of a document)和填充的數據。

然而,實際上還有很多 Web 開發程序員仍是通過生成一大串 HTML 代碼作為響應的形式開發。並且,這不是 PHP 獨有的現實,這是所有 Web 開發程序員都應該重視的。

減少 XSS 漏洞不失為一個好方法。總之,前面談及的瀏覽器安全的章節就顯得十分相關了。簡言之:

  • 儘量避免輸出和輸入(Always escape on output, never on input)。如果你把已清洗的數據(sanitized data)保存在數據庫,然後在其它地方被發現了 SQL 注入漏洞,攻擊者將通過惡意程序汙染這些受信任的已清洗數據(trusted-to-be-sanitized record),從而繞開 XSS 保護。
  • 如果你的框架有一個提供自動上下文過濾的模板引擎,那就使用它吧。這些工作可由框架安全地做到。
  • echo htmlentities($ string,ENT_QUOTES | ENT_HTML5,'UTF-8') 是一種安全、有效的方法阻止UTF-8編碼的網頁上的所有 XSS 攻擊,但不是任何 HTML 都有效。
  • 如果你的環境要求你使用 Markdown 而不是 HTML ,那就不要使用 HTML 了。
  • 如果你需要使用原生 HTML(沒有使用模板引擎),參閱第一點,並且使用 HTML Purifier 吧。HTML Purifier 不適合轉義為 HTML 屬性上下文(HTML attribute context)。

跨站請求偽造

跨站請求偽造(CSRF)是一種混淆的代理攻擊,通過誘導用戶的瀏覽器代表攻擊者執行惡意的 HTTP 請求(使用的是該用戶的權限)。

這在一般情況下是很容易解決的,只需兩步:

  • 使用 HTTPS 。這是先決條件。沒有 HTTPS 的話,任何保護措施都是脆弱的,雖然 HTTPS 本身並不防禦 CSRF 。
  • 增加基本的 Challenge-response authentication。
  • 為每個表單添加一個隱藏的表單屬性。
  • 填充一個密碼安全的隨機值(稱為令牌)。
  • 驗證是否提供了隱藏的表單屬性,以及是否匹配上期望值。

我們寫了一個名為 Anti-CSRF 的庫,並且:

  • 你可以使每個令牌只能使用一次,以防止重放攻擊。
  • 多個令牌存儲在後端。
  • 一旦令牌獲取完,令牌會循環使用。
  • 每個令牌可以綁定特定的 URL 。
  • 如果某個令牌洩露了,它不能在不同的上下文使用。
  • 令牌可以綁定特定的 IP 地址。
  • v2.1 後,令牌可以重複使用(例如供 Ajax 使用)。

如果你沒有使用防止 CSRF 漏洞的框架,請將 Anti-CSRF 放在一邊。在不久的將來,SameSite cookies將允許我們更簡單地避免CSRF攻擊。

XML 攻擊 (XXE, XPath Injection)

在處理大量 XML 的應用程序中存在兩個主要的漏洞:

  • XML External Entities (XXE)
  • XPath 注入

除此之外, XXE 攻擊可用作包含攻擊代碼的本地/遠程文件的啟動器。

早期版本的 Google Docs 被著名於 XXE ,但除了在很大程度上使用 XML 的商業應用程序之外,基本聞所未聞。

針對 XXE 襲擊的主要緩解措施:

 
libxml_disable_entity_loader(true);

除 XML 文檔外,XPath注入與 SQL 注入非常相似。

幸運的是,將用戶輸入傳遞給 XPath 查詢的情況在 PHP 生態中非常罕見。

而不幸的是,這也意味著 PHP 生態中不存在可用的最佳避免措施(預編譯和參數化 XPath 查詢)。最好的辦法是在任何涉及 XPath 查詢的數據上設置允許使用的字符白名單。

declare(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);
}
}
// Usage:
$selected = $xml->xpath(
"/user/username/" . SafeXPathEscaper::allowAlphaNumeric(
$_GET['username']
)
);

白名單總會比黑名單更安全。

反序列化和 PHP 對象注入

深入: 在PHP中安全地實現(反)序列化

如果你將不可信的數據傳遞給unserialize(),則通常是這兩個結果之一:

  • PHP 對象注入,它能用於啟動 POP 鏈(POP chain)並觸發其他誤用對象的漏洞。
  • PHP 解釋器本身的內存損壞。

大多數開發人員更喜歡使用JSON序列化,這是對其軟件安全狀況的顯著改進。但請記住,json_decode()容易受到散列衝突拒絕服務(Hash-DoS)攻擊。不幸的是,PHP的Hash-DOS問題還沒有得到徹底解決。

從djb33遷移到Siphash,對於字符串輸入,哈希輸出的最高位設置為 1 ,對於整數輸入設置為 0 ,使用CSPRNG提供的請求密鑰,將完全解決這些攻擊。

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

因此,最好的辦法是:

  • 使用JSON,因為它比unserialize()更安全。
  • 在任何可能的地方,確保輸入在反序列化之前被認證。
  • 對於提供給用戶的數據,通過一個只有服務器知道的秘鑰使用sodium_crypto_auth()和sodium_crypto_auth_verify()驗證。
  • 對於第三方提供的數據,讓他們使用sodium_crypto_sign()簽名他們的 JSON 消息,然後使用sodium_crypto_sign_open()和第三方公鑰驗證消息。
  • 如果你需要對傳輸的簽名進行十六進制或 Base64 位編碼,也可以使用分離的簽名 API 。
  • 如果你無法驗證 JSON 字符串,請嚴格限制速度並阻止 IP 地址,以減輕重複的違規者。

密碼散列

深入:2016 年,如何安全地保存用戶密碼

安全的密碼存儲曾經是一個激烈爭論的話題,但現在實現起來相當微不足道,特別是在 PHP 中:

$hash = \password_hash($password, PASSWORD_DEFAULT); 

if (\password_verify($password, $hash)) {
// Authenticated.
if (\password_needs_rehash($hash, PASSWORD_DEFAULT)) {
// Rehash, update database.
}
}

你甚至不需要知道在後臺使用什麼算法,因為如果你使用最新版本的 PHP ,你也將使用當前最新的技術,用戶的密碼將會自動進行升級(只要有新的默認算法可用)。

無論你做什麼,都不要做 WordPress 所做的事情。

從 PHP 5.5 到 7.2 ,默認算法都是 Bcrypt 。在未來,它可能會切換到獲得密碼哈希大賽冠軍的 Argon2 。

如果你以前沒有使用password_* API ,那需要遷移遺留哈希,請確保以這種方式進行。很多公司搞錯了, 最有名的是雅虎。 最近,錯誤地實施傳統哈希升級似乎導致了蘋果的iamroot錯誤。

通用加密

這是一些我們詳細寫了的話題:

  • Using Encryption and Authentication Correctly (2015)
  • Recommended: Choosing the Right Cryptography Library for your PHP Project: A Guide (2015)
  • Recommended: You Wouldn't Base64 a Password - Cryptography Decoded (2015)
  • Cryptographically Secure PHP Development (2017)
  • Recommended: Libsodium Quick Reference: Similarly-Named Functions and Their Use-Cases (2017)

一般來說,你總是希望使用 Sodium cryptography library(libsodium)進行應用層加密。如果你需要支持早於 7.2 的 PHP 版本(像 5.2.4),你可以使用sodium_compat,基本上可以假設你的用戶也是 7.2 。

在特定情況下,由於嚴格的算法選擇和互操作性,你可能需要不同的庫。如有疑問,請諮詢密碼專家和密碼工程師,瞭解密碼選擇是否安全(這是我們提供的服務之一)。

隨機性

深入:如何在 PHP 中生成安全的整數和字符串?

如果你需要隨機數字,請使用random_int()。如果你需要隨機字節字符串,請使用random_bytes()。不要使用mt_rand(),rand()或uniqid()。

如果你需要從秘密種子(secret seed)生成偽隨機數(pseudorandom),請使用SeedSpring,而不是srand()或mt_srand()。

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

服務器端 HTTPS 請求

確保 TLS 證書驗證沒有被禁用

隨意使用你已經熟悉的任何兼容 PSR-7 的 HTTP 客戶端。 我們喜歡 Guzzle ,有些人喜歡直接使用 cURL 。

無論你最終使用什麼,請確保使用的確定性,以確保始終可以擁有最新的 CACert 軟件包,從而允許啟用最嚴格的 TLS 證書驗證設置並保護服務器的出站 HTTPS 請求。

安裝 Certainty 很簡單:

composer require paragonie/certainty:^1

使用 Certainty 也很簡單:

 use ParagonIE\Certainty\RemoteFetch;
$latestCACertBundle = (new RemoteFetch())->getLatestBundle();
# cURL users:
$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 users:
/** @var \GuzzleHttp\Client $http */
$repsonse = $http->get(
'https://example.com',
[
'verify' => $latestCACertBundle->getFilePath()
]
);

這樣可以保護你免受網絡服務器與集成的任何第三方 API 之間的中間人攻擊。

我們真的需要 Certainty 嗎?

保護你的系統, Certainty 並不是嚴格的要求。缺少它並不是什麼漏洞。但如果沒有 Certainty ,開源軟件必須猜測操作系統的 CACert 軟件包的存在位置,如果猜測錯誤,它往往會失敗並導致可用性問題。從歷史上看,這激勵了許多開發人員只是禁用證書驗證,以便他們的代碼“正常工作”,卻沒有意識到他們只是將應用程序變成主動攻擊。 Certainty 通過將 CACert 捆綁在最新的可預測位置來消除這種激勵。 Certainty 還為希望運行自己的內部 CA 為企業提供大量的工具。

誰禁用了證書驗證?

流行的內容管理系統(WordPress,Magento 等 CMS)的插件/擴展開發者!這是我們試圖在生態系統層面上解決的一個巨大的問題。 它不是孤立的任何特定的 CMS ,你會發現這些不安全的插件等都是類似的。

如果使用了類似的 CMS ,請在插件中搜索CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST,你可能會發現有幾個將這些值設置為FALSE。

避免的事情

  • 不要使用mcrypt。這是一個十多年來沒有開發出來的密碼學庫。如果你遵循我們的 PHP 版本建議,這應該是一個容易避免的錯誤,因為mcrypt不再被 PHP 7.2 和更新的版本支持。
  • 配置驅動的安全建議應該大部分地忽略。如果你正在閱讀 PHP 安全性指南,並告訴你更改 php.ini 設置而不是編寫更好的代碼,那麼你可能正在閱讀過時的建議。關閉窗口並轉到一些和register_globals無關的文章上吧。
  • 不要使用 JOSE(JWT,JWS,JWE),這是一套互聯網標準,它編纂了一系列容易出錯的密碼設計。儘管由於某種原因,被寫入了標準,也吸引了很多傳道人。
  • 加密 URL 參數是公司常用來模糊元數據的反模式(例如,我們有多少用戶?)。 它帶來了實施錯誤的高風險,也造成了錯誤的安全感。我們在鏈接的文章中提出了一個更安全的選擇。
  • 除非迫不得已,否則不要提供“我忘記了我的密碼”的功能。
  • 不要諱言:密碼重置功能是一個後門。 有一些方法可以實施以抵禦合理的威脅模型,但高風險用戶應該不被考慮。
  • 避免使用 RSA,改用 libsodium 。如果你必須使用 RSA ,請確保指定 OAEP 填充。
  openssl_private_decrypt(
$ciphertext,

$decrypted, // Plaintext gets written to this variable upon success,
$privateKey,
OPENSSL_PKCS1_OAEP_PADDING // Important: DO NOT OMIT THIS!
);

如果你不得不使用 PKCS#1 v1.5 填充,那麼無論你與哪個集成在一起,幾乎肯定會受到 ROBOT 的影響,請以允許明文洩露和簽名偽造的漏洞將其報告給相應的供應商(或 US-CERT )。

專業用法

現在你已經掌握了在 2018 年及以後構建安全 PHP 應用程序的基礎知識,接下來我們來看一些更專業的用法。

可搜索的加密

深入:使用PHP和SQL構建可搜索的加密數據庫

可搜索的加密數據庫是可取的,但被廣泛認為是不太可能實現的。上面鏈接的博客文章試圖通過改進我們解決方案來實現,但本質上是這樣的:

  • 設計你的架構,以便數據庫(database compromise)不會讓攻擊者訪問你的加密密鑰。
  • 用一個密鑰加密數據。
  • 基於 HMAC 或具有靜態鹽的安全 KDF (secure KDF with a static salt)創建多個索引(具有自己獨特的密鑰)
  • 可選:截斷步驟3的輸出,將其用作布隆過濾器(Bloom filter)
  • 在 SELECT 查詢中使用步驟3或4的輸出
  • 解密結果。

在這個過程中的任何一步,你都可以根據實際使用情況進行不同的權衡。

沒有 Side-Channels 的基於令牌的身份驗證

深入: Split Tokens: Token-Based Authentication Protocols without Side-Channels

說到數據庫(上一節),你是否知道 SELECT 查詢理論上可能是定時信息洩漏的來源?

簡單的緩解措施:

  • 把你的認證令牌分為兩半
  • 一半在 SELECT 查詢中使用
  • 後一半在恆定的時間(constant-time)驗證
  • 可以選擇將後半部分的散列存儲在數據庫中。這對於只能使用一次的令牌是有意義的,例如 密碼重置或“在此計算機上記住我”的令牌

即使可以使用定時洩漏來竊取一半的令牌,剩下的也需要暴力破解才能成功。

開發安全的API

深入: Hardening Your PHP-Powered APIs with Sapient

我們寫了 SAPIENT (the Secure API ENgineering Toolkit),讓服務器到服務器驗證的消息傳遞變得簡單易行。除了 HTTPS 提供的安全性之外,Sapient允許你使用共享密鑰或公鑰來加密和驗證消息。 這使得即使存在中間攻擊者,並設有流氓證書頒發機構,你也可以使用Ed25519對 API 請求和響應進行身份驗證,或者將消息加密到只能由接收方服務器的密鑰解密的目標服務器。 由於每個 HTTP 消息體都通過安全密碼進行身份驗證,所以可以安全地使用它來代替stateful token juggling protocols(例如 OAuth)。但是,在密碼學方面,在做任何不規範的事情之前,總要確保他們的實現是由專家研究的。

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

進一步閱讀:

  • Sapient Documentation
  • Sapient Tutorial
  • Sapient Specification

Paragon Initiative Enterprises已經在其許多產品(包括許多開源軟件項目)中使用了Sapient,

並將繼續添加軟件項目到Sapient用戶群中。

使用Chronicle記錄安全事件

深入: Chronicle Will Make You Question the Need for Blockchain Technology

Chronicle是一個基於散列鏈數據結構的僅追加密碼分類賬(append-only cryptographic ledger),具有很多吸引公司“區塊鏈”技術的屬性,而不會過分矯枉過正。

除了僅追加密碼分類賬(append-only cryptographic ledger)這個具有創造性的用例之外,Chronicle集成到SIEM中時,也可以十分有亮點,因為你可以將安全關鍵事件發送到私人Chronicle中,並且它們是不能被改變的。

如果你的Chronicle設置為將其摘要散列交叉簽名到其他Chronicle實例,或者如果有其他實例配置為複製你的Chronicle內容,攻擊者就很難篡改你的安全事件日誌。

在Chronicle的幫助下,你可以獲得區塊鏈所承諾的彈性特性(resilience),而沒有任何隱私,性能或可伸縮性問題。

要將數據發佈到本地Chronicle,你可以使用任何與Sapient-compatible API,但最簡單的解決方案稱為Quill。

作者的一些話

一些聰明的讀者可能注意到我們引用了很多我們自己的工作,包括博客文章和開源軟件。(當然也不僅僅引用了我們自己的工作)

這絕不是偶然的。

自從我們在 2015 年初成立以來,一直在編寫安全庫並參與提高 PHP 生態系統安全性的工作。我們已經涉足了很多領域,而且我們的安全工程師(他們最近推動了更安全的加密技術加入 PHP 核心,就在最近的 PHP 7.2 中)自我擔保地說,並不擅長自我炒作,或是對已經做過的工作持續熱情。但你很可能沒有聽說我們多年來開發的工具或庫。對於這個,深感抱歉。

不論如何,我們也不可能成為各方面的先行者,所以我們儘可能地選擇與重視公共利益而不是貪圖小利的行業專家工作。

這也是為什麼瀏覽器安全的許多章節都參考了 Scott Helme 和他公司的工作,他們在為開發人員提供這些新的安全功能方面具有可訪問性和可理解性。

本指南當然不會是詳盡的。編寫不安全代碼的方法幾乎和編寫代碼的方法一樣多。 安全是一種心態,而不是目的地。

隨著上面所寫的一切,以及後面涉及的資源,我們希望這將有助於全世界的開發人員,從今天開始用 PHP 編寫安全的軟件。

資源

如果你已經按照本頁上的所有內容進行了操作,並且需要更多內容,則可能會對我們策劃的閱讀列表感興趣,以便學習應用程序安全性。

如果你認為自己編寫的代碼足夠安全,並希望我們從安全工程師的角度對其進行評判,這也是我們為客戶提供的服務。

你如果為一家要進行合規性測試(PCI-DSS,ISO 27001等)的公司工作,可能還想聘請我們公司來審核你的源代碼。我們的流程比其他安全諮詢公司更適合開發者。

接下來是 PHP 和信息安全社區提供的資源列表,這些資源幫助互聯網更加安全。

  • PHP: The Right Way:現代 PHP 開發的實用指南,免費在線。
  • Mozilla's SSL Config Generator
  • Let's Encrypt:證書頒發機構,通過提供免費 TLS 證書,為創建更安全的 Internet 做了很多。
  • Qualys SSL Labs:為 TLS 配置提供了一個快速而簡單的測試套件。幾乎每個人都使用這個來解決他們的密碼組和證書問題,理由很充分:
    It does its job well.
  • Security Headers:可以檢驗你的網站在使用瀏覽器安全功能來保護用戶方面的表現如何。
  • Report-URI:一個很好的免費資源,提供監控 CSP/HPKP 等安全策略的實時安全報告服務。他們給你一個 Report-URI,你可以傳遞給你的用戶的瀏覽器,如果有什麼事情發生或有人發現 XSS 攻擊媒介,他們會投訴Report-URI。 Report-URI 會彙總這些錯誤,並允許你更好地對這些報告進行疑難解答和分類。
  • PHP Security Advent Calenda:RIPSTech旗下的團隊負責。
  • Snuffleupagus:一個面向安全的 PHP 模塊(Suhosin的精神繼承者,似乎在很大程度上會被放棄)
  • PHP Delusions:一個致力於更好地使用 PHP 的網站。大部分的口吻是非常有見地的,作者對技術的準確性和清晰度的奉獻使得值得一讀,特別是對於那些不太喜歡 PDO 功能的人來說。
  • Have I Been Pwned?:幫助用戶發現他們的數據是否屬於過時數據洩露。

結尾

原文地址:The 2018 Guide to Building Secure PHP Software - P.I.E. Staff


分享到:


相關文章: