The 2018 Guide to Building Secure PHP Software惕它!
目錄
- 前言
- PHP 版本 / PHP Versions
- Composer 依賴管理 / Dependency Management with Composer
- HTTPS 和瀏覽器安全 / HTTPS and Browser Security
-
開發(fā)安全的 PHP 程序 / Developing Secure PHP Software
- 數(shù)據(jù)庫注入 / Database Interaction
- 文件上傳 / File Uploads
- 跨站腳本 / Cross-Site Scripting (XSS)
- 跨站請(qǐng)求偽造 / Cross-Site Request Forgery (CSRF)
- XML 攻擊 / XML attacks (XXE, XPath Injection)
- 反序列化和 PHP 對(duì)象注入 / Deserialization and PHP Object Injection
- 密碼散列 / Password Hashing
- 通用加密 / General-Purpose Cryptography
- 隨機(jī)性 / Randomness
- 服務(wù)器端 HTTPS 請(qǐng)求 / Server-Side HTTPS Requests
- 避免的事情 / Things to Avoid
- 專業(yè)用法 / Specialized Use-Cases
- 作者的一些話 / A Word From the Author
- 資源 / Resources
前言
2018 年將至,一般程序員(特別是 Web 開發(fā)程序員)應(yīng)當(dāng)拋棄過去開發(fā)PHP程序的很多不好的習(xí)慣和觀念了。雖然部分人不以為意,但是這確實(shí)是事實(shí)。
這個(gè)指南應(yīng)該以重點(diǎn)部分作為 PHP: The Right Way 安全章節(jié)的補(bǔ)充,而不是以一般的 PHP 編程話題。
正文
PHP 版本
請(qǐng)?jiān)?2018 年使用 PHP 7.2, 并且計(jì)劃 2019 年初切換到 PHP 7.3闸准。
PHP 7.2 已于 2017 年 11 月 30 日發(fā)布。
寫這篇文章的時(shí)候梢灭,只有 7.1 和 7.2 版本還在被 PHP 官方積極維護(hù)夷家,而 5.6 和 7.0 只在大概1年內(nèi)提供安全補(bǔ)丁更新。
對(duì)于其他官方不維護(hù)的 PHP 版本敏释,雖然某些操作系統(tǒng)會(huì)提供長期支持和維護(hù)库快,但這其實(shí)通常是有害的。尤其是他們提供安全支持補(bǔ)丁卻沒有版本號(hào)钥顽,這使得很難解釋系統(tǒng)的安全性(僅僅知道 PHP 版本)义屏。
因此,無論其他供應(yīng)商提出了什么承諾蜂大,如果可以闽铐,你就應(yīng)該在任何時(shí)候都堅(jiān)決地使用官方提供支持的 PHP 版本。這樣奶浦,盡管最終是一個(gè)短暫的安全版本兄墅,但一個(gè)不斷致力于升級(jí)的版本,總會(huì)讓你收獲一些意外的驚喜澳叉。
依賴管理
人生苦短察迟,我用 Composer
在 PHP 生態(tài)中,Composer 是最先進(jìn)的依賴管理方案耳高。我們推薦 PHP: The Right Way 中關(guān)于依賴管理的完整章節(jié)。
如果你沒有使用 Composer 來管理應(yīng)用的依賴所踊,最終(hopefully later but most likely sooner)會(huì)導(dǎo)致應(yīng)用里某個(gè)依賴會(huì)嚴(yán)重過時(shí)泌枪,然后老舊版本中的漏洞會(huì)被利用于計(jì)算機(jī)犯罪。
重要: 開發(fā)軟件時(shí)秕岛,時(shí)常記得保持依賴的更新碌燕。幸運(yùn)地,這只需一行命令:
composer update
如果你正在使用某些專業(yè)的继薛,需要使用 PHP 擴(kuò)展(C 語言編寫)修壕,那你不能使用 Composer 管理,而需要 PECL 遏考。
推薦擴(kuò)展
不管你正在編寫什么慈鸠,你總會(huì)受益于這些依賴。這是除了大多數(shù) PHP 程序員的推薦(PHPUnit, PHP-CS-Fixer, ...)外的補(bǔ)充灌具。
roave/security-advisories
Roave's security-advisories 使用 Friends of PHP repository 確保你的項(xiàng)目沒有依賴一些已知易受攻擊的依賴青团。
composer require roave/security-advisories:dev-master
或者譬巫,你可以上傳你的composer.lock
文件到 Sensio Labs ,作為例行自動(dòng)化漏洞評(píng)估工作流的一部分督笆,以提醒發(fā)現(xiàn)任何過時(shí)的軟件包芦昔。
vimeo/psalm
Psalm 是一個(gè)幫助你識(shí)別代碼里可能存在 bugs 的靜態(tài)分析工具。還有其他很好的靜態(tài)分析工具(例如 Phan 和 PHPStan 都很棒)娃肿,但當(dāng)你發(fā)現(xiàn)你需要支持 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
如果你是第一次在現(xiàn)有代碼庫運(yùn)行料扰,可能會(huì)看到很多紅色錯(cuò)誤凭豪。但除非你在構(gòu)建像 WordPress 那么大的程序,否則努力通過所有測(cè)試絕不是艱巨的记罚。
無論使用哪種靜態(tài)分析工具墅诡,我們都推薦你能將他加入到持續(xù)集成工作流(Continuous Integration workflow)中,以便在每次更改代碼中運(yùn)行桐智。
HTTPS 和瀏覽器安全
HTTPS, which should be tested, and security headers .
2018 年末早,不安全的 HTTP 網(wǎng)站將不再被接受。幸運(yùn)的是说庭,由于 ACME 協(xié)議 和 Let's Encrypt certificate authority然磷,免費(fèi)的 TLS 證書成為了可能。
將 ACME 集成到你的服務(wù)器刊驴,小菜一碟姿搜。
- Caddy: 自動(dòng)加入。
-
Apache: 很快作為
mod_md
可用捆憎。在此之前舅柜,網(wǎng)上很多高質(zhì)量教程。 - Nginx: 相對(duì)簡單躲惰。
你也許會(huì)想致份,“好,我已經(jīng)有 TLS 證書了础拨,為了網(wǎng)站變得安全和快速氮块,得花些時(shí)間折騰配置信息」钭冢”
不滔蝉!Mozilla做了件好事情!塔沃。你可以根據(jù)網(wǎng)站的目標(biāo)受眾蝠引,使用配置生成器生成推薦套件。
如果你希望網(wǎng)站安全,HTTPS ( HTTP over TLS ) 是絕對(duì)不能妥協(xié)的立肘。使用 HTTPS 立刻就能消除多種攻擊(中間人攻擊边坤、竊聽、重放攻擊以及若干允許用戶模仿的會(huì)話形式的攻擊)谅年。
安全頭
在服務(wù)器使用 HTTPS 確實(shí)為用戶提供了許多安全性和性能方面的好處茧痒,但也還能通過利用某些瀏覽器的安全功能來進(jìn)一步提升安全性。而這大部分會(huì)涉及到響應(yīng)內(nèi)容的安全頭融蹂。
-
Content-Security-Policy
- 你需要該 Header 旺订,因?yàn)樗峁┝藢?duì)于瀏覽器是否允許加載內(nèi)部和外部資源的細(xì)化控制,從而為跨域腳本攻擊漏洞提供了有效防御層超燃。
- 參閱 CSP-Builder区拳,以便快速簡便地部署/管理內(nèi)容安全策略(Content Security Policies)。
- 為了更加深入的分析意乓, Scott Helme's introduction to Content-Security-Policy headers樱调,會(huì)是一個(gè)很好的引導(dǎo)。
-
Expect-CT
- 你需要該 Header 届良,因?yàn)樗芡ㄟ^強(qiáng)制某些不良行為者將其錯(cuò)誤證書的證據(jù)頒發(fā)到可公開驗(yàn)證的僅可追加的數(shù)據(jù)結(jié)構(gòu)笆凌,從而針對(duì)流氓/受損的證書頒發(fā)機(jī)構(gòu)增加一層防護(hù)。
- 優(yōu)先設(shè)置為
enforce,max-age=30
士葫。只要你有足夠的自信該 Header 不會(huì)造成服務(wù)中斷乞而,增加max-age
吧。
-
Referrer-Policy
- 你需要該 Header 慢显,因?yàn)樗试S你控制用戶的行為信息是否泄露給第三方爪模。
- 同樣地,Scott Helme 提供了一篇關(guān)于Referrer-Policy Header 介紹好文荚藻。
- 除非有理由允許更加寬松的設(shè)置屋灌,否則請(qǐng)?jiān)O(shè)置為
same-origin
或no-referrer
。
-
Strict-Transport-Security
- 你需要該 Header 应狱,因?yàn)樗嬖V瀏覽器通過 HTTPS 而不是不安全的 HTTP 共郭,將 future requests 設(shè)為同源。
- 在第一次部署時(shí)侦香,將其設(shè)置為
max-age = 30
,然后當(dāng)你確信沒有任何內(nèi)容會(huì)中斷時(shí)纽疟,將此值增加到某個(gè)較大的值(例如 31536000)罐韩。
-
X-Content-Type-Options
- 你需要該 Header ,因?yàn)?MIME 類型的混淆可能會(huì)導(dǎo)致不可預(yù)知的結(jié)果污朽,包括奇怪的允許 XSS 漏洞的邊緣情況散吵。這最好伴隨著一個(gè)標(biāo)準(zhǔn)的 Content-Type Header 。
- 除非需要默認(rèn)的行為(例如文件的下載),否則請(qǐng)?jiān)O(shè)置為
nosniff
矾睦。
-
X-Frame-Options
- 你需要該 Header 晦款,因?yàn)樗试S你防止點(diǎn)擊劫持。
- 設(shè)置為
DENY
(或者SAMEORIGIN
, 但僅僅當(dāng)你使用<frame>
元素的時(shí)候)枚冗。
-
X-XSS-Protection
- 你需要該 Header 缓溅,因?yàn)樗鼏⒂昧艘恍┠J(rèn)情況下未啟用的瀏覽器反 XSS 功能。
- 設(shè)置為
1; mode=block
赁温。
同樣坛怪,如果你使用 PHP 的內(nèi)置會(huì)話管理功能(建議使用),則可能需要這樣調(diào)用session_start()
:
<?php
session_start([
'cookie_httponly' => true,
'cookie_secure' => true
]);
這會(huì)強(qiáng)制你的應(yīng)用在發(fā)送會(huì)話標(biāo)識(shí)符時(shí)使用 HTTP-Only 和 Secure 標(biāo)志股囊,從而防止 XSS 攻擊竊取用戶的 Cookie 袜匿,并強(qiáng)制它們分別通過 HTTPS 發(fā)送。 我們之前在 2015 年的博客文章中介紹了安全的 PHP 會(huì)話稚疹。
子資源完整性
在將來的某個(gè)時(shí)候居灯,你也許會(huì)使用 CDN 來加載網(wǎng)站的公共 JavaScript/CSS 庫。安全工程師已經(jīng)遇見了這存在一個(gè)明顯的風(fēng)險(xiǎn)内狗,如果很多網(wǎng)站使用 CDN 提供內(nèi)容怪嫌,Hack 和替換 CDN(獲得了 CDN 的控制權(quán))就可以注入(惡意)代碼到成千上萬的網(wǎng)站。
查閱子資源完整性吧其屏。
子資源完整性(SRI喇勋,Subresource integrity)允許你將希望 CDN 服務(wù)的文件的內(nèi)容進(jìn)行哈希處理。目前實(shí)行的 SRI 只允許使用安全的密碼散列函數(shù)偎行,這意味著攻擊者不可能生成與原始文件哈希相同的惡意版本資源川背。
一個(gè)真實(shí)例子: Bootstrap v4-alpha uses SRI in their CDN example snippet
<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)系
Web 開發(fā)人員經(jīng)常在超鏈接上設(shè)置目標(biāo)屬性(例如,target ="_ blank"
在新窗口中打開鏈接)蛤袒。但是熄云,如果你沒有傳遞rel ="noopener"
標(biāo)簽,則可以允許目標(biāo)頁面控制當(dāng)前頁面妙真。
不要這樣做:
<a target="_blank">Click here</a>
這會(huì)讓http://example.com
頁面能控制當(dāng)前頁面缴允。
而應(yīng)該這樣做:
<a target="_blank" rel="noopener noreferrer">Click here</a>
通過這樣在新窗口打開https://example.com
,當(dāng)前窗口的控制權(quán)也不會(huì)授予可能的惡意第三方珍德。
可以更加深入研究练般。
開發(fā)安全的 PHP 程序
如果應(yīng)用程序安全性對(duì)你來說是一個(gè)新話題,請(qǐng)從應(yīng)用程序安全性簡介開始吧锈候。
大多數(shù)安全專家指出薄料,開發(fā)者可以使用 OWASP Top 10 等資源開始著手。
但是泵琳,大多數(shù)常見的漏洞也可以是相同高等級(jí)的安全問題(例如代碼和數(shù)據(jù)沒有完全分離摄职、邏輯不嚴(yán)謹(jǐn)和健全誊役、操作環(huán)境不安全或是可破譯的密碼協(xié)議等)。
我們的假設(shè)是蛔垢,應(yīng)該授予安全新手知道一些更簡單、基礎(chǔ)的安全知識(shí)和問題迫悠,并如何解決這些問題,應(yīng)該是一個(gè)更好的碍拆、長遠(yuǎn)的安全工程。
因此滑进,我們避免推薦十大或二十大安全清單。
數(shù)據(jù)庫注入
如果你是自己編寫 SQL 代碼缆镣,請(qǐng)確保使用prepared
語句抄伍,并且從網(wǎng)絡(luò)或文件系統(tǒng)提供的信息都作為參數(shù)傳遞钱床,而不是字符串拼接的形式僧免。此外,確保你沒有使用模擬的prepared語句毛肋。
為了達(dá)到好的效果厂财,可以使用 EasyDB 裆甩。
不要這樣做:
<?php
/* Insecure code: */
$query = $pdo->query("SELECT * FROM users WHERE username = '" . $_GET['username'] . "'");
應(yīng)該這樣做:
<?php
/* Secure against SQL injection: */
$results = $easydb->row("SELECT * FROM users WHERE username = ?", $_GET['username']);
還有其他數(shù)據(jù)庫抽象層提供了相同的安全性(EasyDB實(shí)際上是在使用 PDO 樱蛤,但在實(shí)際的prepare
語句前避免了prepared
語句模擬)蚁署。 只要用戶輸入不會(huì)影響查詢的結(jié)構(gòu)奸笤,就很安全(包括存儲(chǔ)過程)扣癣。
文件上傳
接受文件上傳是一個(gè)冒險(xiǎn)的提議呜魄,但只要采取一些基本的預(yù)防措施,是能保證安全的莱衩。也就是說爵嗅,允許文件直接上傳的話,這些文件可能會(huì)被意外的允許執(zhí)行或解釋笨蚁。上傳的文件應(yīng)該是只讀(read-only)或讀寫(read-write)的睹晒,永遠(yuǎn)不應(yīng)該可執(zhí)行(executable)。
如果你的網(wǎng)站根目錄是/var/www/example.com
括细,請(qǐng)不要保存上傳文件在/var/www/example.com/uploaded_files
册招。
而應(yīng)該保存到一個(gè)不能直接訪問的目錄(例如:/var/www/example.com-uploaded/
),以免意外地將其作為服務(wù)器端腳本執(zhí)行勒极,并獲得執(zhí)行遠(yuǎn)程代碼的后門是掰。
一個(gè)更加簡潔的方法是將網(wǎng)站根目錄往下移動(dòng)一個(gè)層級(jí)(即:/var/www/example.com/public
)。
如何安全地下載這些上傳文件也是一個(gè)問題辱匿。
- 直接訪問 SVG 圖像類型時(shí)键痛,將在用戶瀏覽器執(zhí)行 JavaScript 代碼炫彩。盡管它的MIME類型中的
image/
前綴具有誤導(dǎo)性,但是這是正確的絮短。 - 正如前面提及的江兢,MIME 類型嗅探可能導(dǎo)致類型混淆攻擊。請(qǐng)參閱X-Content-Type-Options丁频。
- 如果你放棄前面關(guān)于如何安全地存儲(chǔ)上傳文件的建議杉允,攻擊者就會(huì)通過上傳 .php 或 .phtml 文件,直接在瀏覽器中訪問文件來執(zhí)行任意代碼席里,從而完全控制服務(wù)器叔磷。
跨站腳本
同樣地奖磁,預(yù)防 XSS 和 SQL 注入是一樣簡單的改基。我們有簡單而易用的 API 來分離文檔結(jié)構(gòu)(structure of a document)和填充的數(shù)據(jù)。
然而咖为,實(shí)際上還有很多 Web 開發(fā)程序員仍是通過生成一大串 HTML 代碼作為響應(yīng)的形式開發(fā)秕狰。并且,這不是 PHP 獨(dú)有的現(xiàn)實(shí)躁染,這是所有 Web 開發(fā)程序員都應(yīng)該重視的鸣哀。
減少 XSS 漏洞不失為一個(gè)好方法⊥掏總之诺舔,前面談及的瀏覽器安全的章節(jié)就顯得十分相關(guān)了。簡言之:
- 盡量避免輸出和輸入(
Always escape on output, never on input
)备畦。如果你把已清洗的數(shù)據(jù)(sanitized data)保存在數(shù)據(jù)庫低飒,然后在其它地方被發(fā)現(xiàn)了 SQL 注入漏洞,攻擊者將通過惡意程序污染這些受信任的已清洗數(shù)據(jù)(trusted-to-be-sanitized record)懂盐,從而繞開 XSS 保護(hù)褥赊。 - 如果你的框架有一個(gè)提供自動(dòng)上下文過濾的模板引擎,那就使用它吧莉恼。這些工作可由框架安全地做到拌喉。
-
echo htmlentities($ string,ENT_QUOTES | ENT_HTML5俐银,'UTF-8')
是一種安全尿背、有效的方法阻止UTF-8編碼的網(wǎng)頁上的所有 XSS 攻擊,但不是任何 HTML 都有效捶惜。 - 如果你的環(huán)境要求你使用 Markdown 而不是 HTML 田藐,那就不要使用 HTML 了。
- 如果你需要使用原生 HTML(沒有使用模板引擎),參閱第一點(diǎn)汽久,并且使用 HTML Purifier 吧鹤竭。HTML Purifier 不適合轉(zhuǎn)義為 HTML 屬性上下文(HTML attribute context)。
跨站請(qǐng)求偽造
跨站請(qǐng)求偽造(CSRF)是一種混淆的代理攻擊景醇,通過誘導(dǎo)用戶的瀏覽器代表攻擊者執(zhí)行惡意的 HTTP 請(qǐng)求(使用的是該用戶的權(quán)限)臀稚。
這在一般情況下是很容易解決的,只需兩步:
- 使用 HTTPS 三痰。這是先決條件吧寺。沒有 HTTPS 的話,任何保護(hù)措施都是脆弱的散劫,雖然 HTTPS 本身并不防御 CSRF 稚机。
- 增加基本的 Challenge-response authentication。
- 為每個(gè)表單添加一個(gè)隱藏的表單屬性舷丹。
- 填充一個(gè)密碼安全的隨機(jī)值(稱為令牌)。
- 驗(yàn)證是否提供了隱藏的表單屬性蜓肆,以及是否匹配上期望值颜凯。
我們寫了一個(gè)名為 Anti-CSRF 的庫,并且:
- 你可以使每個(gè)令牌只能使用一次仗扬,以防止重放攻擊症概。
- 多個(gè)令牌存儲(chǔ)在后端。
- 一旦令牌獲取完早芭,令牌會(huì)循環(huán)使用彼城。
- 每個(gè)令牌可以綁定特定的 URL 。
- 如果某個(gè)令牌泄露了退个,它不能在不同的上下文使用募壕。
- 令牌可以綁定特定的 IP 地址。
- v2.1 后语盈,令牌可以重復(fù)使用(例如供 Ajax 使用)舱馅。
如果你沒有使用防止 CSRF 漏洞的框架,請(qǐng)將 Anti-CSRF 放在一邊刀荒。在不久的將來代嗤,SameSite cookies將允許我們更簡單地避免CSRF攻擊。
XML 攻擊 (XXE, XPath Injection)
在處理大量 XML 的應(yīng)用程序中存在兩個(gè)主要的漏洞:
- XML External Entities (XXE)
- XPath 注入
除此之外缠借, XXE 攻擊可用作包含攻擊代碼的本地/遠(yuǎn)程文件的啟動(dòng)器干毅。
早期版本的 Google Docs 被著名于 XXE ,但除了在很大程度上使用 XML 的商業(yè)應(yīng)用程序之外泼返,基本聞所未聞硝逢。
針對(duì) XXE 襲擊的主要緩解措施:
<?php
libxml_disable_entity_loader(true);
除 XML 文檔外,XPath注入與 SQL 注入非常相似。
幸運(yùn)的是趴捅,將用戶輸入傳遞給 XPath 查詢的情況在 PHP 生態(tài)中非常罕見垫毙。
而不幸的是,這也意味著 PHP 生態(tài)中不存在可用的最佳避免措施(預(yù)編譯和參數(shù)化 XPath 查詢)拱绑。最好的辦法是在任何涉及 XPath 查詢的數(shù)據(jù)上設(shè)置允許使用的字符白名單综芥。
<?php
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']
)
);
白名單總會(huì)比黑名單更安全。
反序列化和 PHP 對(duì)象注入
如果你將不可信的數(shù)據(jù)傳遞給unserialize()
猎拨,則通常是這兩個(gè)結(jié)果之一:
- PHP 對(duì)象注入膀藐,它能用于啟動(dòng) POP 鏈(POP chain)并觸發(fā)其他誤用對(duì)象的漏洞。
- PHP 解釋器本身的內(nèi)存損壞红省。
大多數(shù)開發(fā)人員更喜歡使用JSON序列化额各,這是對(duì)其軟件安全狀況的顯著改進(jìn)。但請(qǐng)記住吧恃,json_decode()
容易受到散列沖突拒絕服務(wù)(Hash-DoS)攻擊虾啦。不幸的是,PHP的Hash-DOS問題還沒有得到徹底解決痕寓。
從djb33
遷移到Siphash
傲醉,對(duì)于字符串輸入,哈希輸出的最高位設(shè)置為 1 呻率,對(duì)于整數(shù)輸入設(shè)置為 0 硬毕,使用CSPRNG
提供的請(qǐng)求密鑰,將完全解決這些攻擊礼仗。
不幸的是吐咳, PHP 團(tuán)隊(duì)還沒有準(zhǔn)備好放棄他們已經(jīng)在 PHP 7 系列中取得的性能提升,所以很難說服他們放棄 djb33 (這是非吃快但不安全的) 贊成 SipHash (這也是快速的韭脊,但不像 djb33 那么快,但更安全)单旁。 如果性能受到重大影響乾蓬,可能會(huì)阻礙未來版本的采用,但也影響了安全性慎恒。
因此任内,最好的辦法是:
- 使用
JSON
,因?yàn)樗?code>unserialize()更安全融柬。 - 在任何可能的地方死嗦,確保輸入在反序列化之前被認(rèn)證。
- 對(duì)于提供給用戶的數(shù)據(jù)粒氧,通過一個(gè)只有服務(wù)器知道的秘鑰使用
sodium_crypto_auth()
和sodium_crypto_auth_verify()
驗(yàn)證越除。 - 對(duì)于第三方提供的數(shù)據(jù),讓他們使用
sodium_crypto_sign()
簽名他們的 JSON 消息,然后使用sodium_crypto_sign_open()
和第三方公鑰驗(yàn)證消息摘盆。 - 如果你需要對(duì)傳輸?shù)暮灻M(jìn)行十六進(jìn)制或 Base64 位編碼翼雀,也可以使用分離的簽名 API 。
- 對(duì)于提供給用戶的數(shù)據(jù)粒氧,通過一個(gè)只有服務(wù)器知道的秘鑰使用
- 如果你無法驗(yàn)證 JSON 字符串孩擂,請(qǐng)嚴(yán)格限制速度并阻止 IP 地址狼渊,以減輕重復(fù)的違規(guī)者。
密碼散列
安全的密碼存儲(chǔ)曾經(jīng)是一個(gè)激烈爭論的話題拗秘,但現(xiàn)在實(shí)現(xiàn)起來相當(dāng)微不足道指巡,特別是在 PHP 中:
<?php
$hash = \password_hash($password, PASSWORD_DEFAULT);
if (\password_verify($password, $hash)) {
// Authenticated.
if (\password_needs_rehash($hash, PASSWORD_DEFAULT)) {
// Rehash, update database.
}
}
你甚至不需要知道在后臺(tái)使用什么算法律姨,因?yàn)槿绻闶褂米钚掳姹镜?PHP 限寞,你也將使用當(dāng)前最新的技術(shù),用戶的密碼將會(huì)自動(dòng)進(jìn)行升級(jí)(只要有新的默認(rèn)算法可用)砰琢。
無論你做什么蘸嘶,都不要做 WordPress 所做的事情。
從 PHP 5.5 到 7.2 陪汽,默認(rèn)算法都是 Bcrypt 训唱。在未來,它可能會(huì)切換到獲得密碼哈希大賽冠軍的 Argon2 掩缓。
如果你以前沒有使用password_*
API 雪情,那需要遷移遺留哈希遵岩,請(qǐng)確保以這種方式進(jìn)行你辣。很多公司搞錯(cuò)了, 最有名的是雅虎尘执。 最近舍哄,錯(cuò)誤地實(shí)施傳統(tǒng)哈希升級(jí)似乎導(dǎo)致了蘋果的iamroot錯(cuò)誤。
通用加密
這是一些我們?cè)敿?xì)寫了的話題:
- 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)進(jìn)行應(yīng)用層加密表悬。如果你需要支持早于 7.2 的 PHP 版本(像 5.2.4),你可以使用sodium_compat丧靡,基本上可以假設(shè)你的用戶也是 7.2 蟆沫。
在特定情況下,由于嚴(yán)格的算法選擇和互操作性温治,你可能需要不同的庫饭庞。如有疑問,請(qǐng)咨詢密碼專家和密碼工程師熬荆,了解密碼選擇是否安全(這是我們提供的服務(wù)之一)舟山。
隨機(jī)性
如果你需要隨機(jī)數(shù)字,請(qǐng)使用random_int()
累盗。如果你需要隨機(jī)字節(jié)字符串寒矿,請(qǐng)使用random_bytes()
。不要使用mt_rand()
若债,rand()
或uniqid()
符相。
如果你需要從秘密種子(secret seed)生成偽隨機(jī)數(shù)(pseudorandom),請(qǐng)使用SeedSpring拆座,而不是srand()
或mt_srand()
主巍。
<?php
use ParagonIE\SeedSpring\SeedSpring;
$seed = random_bytes(16);
$rng = new SeedSpring($seed);
$data = $rng->getBytes(1024);
$int = $rng->getInt(1, 100);
服務(wù)器端 HTTPS 請(qǐng)求
確保 TLS 證書驗(yàn)證沒有被禁用
隨意使用你已經(jīng)熟悉的任何兼容 PSR-7 的 HTTP 客戶端。 我們喜歡 Guzzle 挪凑,有些人喜歡直接使用 cURL 孕索。
無論你最終使用什么,請(qǐng)確保使用的確定性躏碳,以確保始終可以擁有最新的 CACert 軟件包搞旭,從而允許啟用最嚴(yán)格的 TLS 證書驗(yàn)證設(shè)置并保護(hù)服務(wù)器的出站 HTTPS 請(qǐng)求。
安裝 Certainty 很簡單:
composer require paragonie/certainty:^1
使用 Certainty 也很簡單:
<?php
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()
]
);
這樣可以保護(hù)你免受網(wǎng)絡(luò)服務(wù)器與集成的任何第三方 API 之間的中間人攻擊菇绵。
我們真的需要 Certainty 嗎肄渗?
保護(hù)你的系統(tǒng), Certainty 并不是嚴(yán)格的要求咬最。缺少它并不是什么漏洞翎嫡。但如果沒有 Certainty ,開源軟件必須猜測(cè)操作系統(tǒng)的 CACert 軟件包的存在位置永乌,如果猜測(cè)錯(cuò)誤惑申,它往往會(huì)失敗并導(dǎo)致可用性問題。從歷史上看翅雏,這激勵(lì)了許多開發(fā)人員只是禁用證書驗(yàn)證圈驼,以便他們的代碼“正常工作”,卻沒有意識(shí)到他們只是將應(yīng)用程序變成主動(dòng)攻擊望几。 Certainty 通過將 CACert 捆綁在最新的可預(yù)測(cè)位置來消除這種激勵(lì)绩脆。 Certainty 還為希望運(yùn)行自己的內(nèi)部 CA 為企業(yè)提供大量的工具。
誰禁用了證書驗(yàn)證橄抹?
流行的內(nèi)容管理系統(tǒng)(WordPress靴迫,Magento 等 CMS)的插件/擴(kuò)展開發(fā)者!這是我們?cè)噲D在生態(tài)系統(tǒng)層面上解決的一個(gè)巨大的問題楼誓。 它不是孤立的任何特定的 CMS 玉锌,你會(huì)發(fā)現(xiàn)這些不安全的插件等都是類似的。
如果使用了類似的 CMS 慌随,請(qǐng)?jiān)诓寮兴阉?code>CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST
芬沉,你可能會(huì)發(fā)現(xiàn)有幾個(gè)將這些值設(shè)置為FALSE
躺同。
避免的事情
-
不要使用
mcrypt
。這是一個(gè)十多年來沒有開發(fā)出來的密碼學(xué)庫丸逸。如果你遵循我們的 PHP 版本建議蹋艺,這應(yīng)該是一個(gè)容易避免的錯(cuò)誤,因?yàn)?code>mcrypt不再被 PHP 7.2 和更新的版本支持黄刚。 -
配置驅(qū)動(dòng)的安全建議應(yīng)該大部分地忽略捎谨。如果你正在閱讀 PHP 安全性指南,并告訴你更改 php.ini 設(shè)置而不是編寫更好的代碼憔维,那么你可能正在閱讀過時(shí)的建議涛救。關(guān)閉窗口并轉(zhuǎn)到一些和
register_globals
無關(guān)的文章上吧。 - 不要使用 JOSE(JWT业扒,JWS检吆,JWE),這是一套互聯(lián)網(wǎng)標(biāo)準(zhǔn)程储,它編纂了一系列容易出錯(cuò)的密碼設(shè)計(jì)蹭沛。盡管由于某種原因,被寫入了標(biāo)準(zhǔn)章鲤,也吸引了很多傳道人摊灭。
- 加密 URL 參數(shù)是公司常用來模糊元數(shù)據(jù)的反模式(例如,我們有多少用戶败徊?)帚呼。 它帶來了實(shí)施錯(cuò)誤的高風(fēng)險(xiǎn),也造成了錯(cuò)誤的安全感皱蹦。我們?cè)阪溄拥奈恼轮刑岢隽艘粋€(gè)更安全的選擇煤杀。
- 除非迫不得已,否則不要提供“我忘記了我的密碼”的功能根欧。
不要諱言:密碼重置功能是一個(gè)后門怜珍。 有一些方法可以實(shí)施以抵御合理的威脅模型端蛆,但高風(fēng)險(xiǎn)用戶應(yīng)該不被考慮凤粗。 - 避免使用 RSA,改用 libsodium 今豆。如果你必須使用 RSA 嫌拣,請(qǐng)確保指定 OAEP 填充。
<?php
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 填充呆躲,那么無論你與哪個(gè)集成在一起异逐,幾乎肯定會(huì)受到 ROBOT 的影響,請(qǐng)以允許明文泄露和簽名偽造的漏洞將其報(bào)告給相應(yīng)的供應(yīng)商(或 US-CERT )插掂。
專業(yè)用法
現(xiàn)在你已經(jīng)掌握了在 2018 年及以后構(gòu)建安全 PHP 應(yīng)用程序的基礎(chǔ)知識(shí)灰瞻,接下來我們來看一些更專業(yè)的用法腥例。
可搜索的加密
可搜索的加密數(shù)據(jù)庫是可取的,但被廣泛認(rèn)為是不太可能實(shí)現(xiàn)的酝润。上面鏈接的博客文章試圖通過改進(jìn)我們解決方案來實(shí)現(xiàn)燎竖,但本質(zhì)上是這樣的:
- 設(shè)計(jì)你的架構(gòu),以便數(shù)據(jù)庫(database compromise)不會(huì)讓攻擊者訪問你的加密密鑰要销。
- 用一個(gè)密鑰加密數(shù)據(jù)构回。
- 基于 HMAC 或具有靜態(tài)鹽的安全 KDF (secure KDF with a static salt)創(chuàng)建多個(gè)索引(具有自己獨(dú)特的密鑰)
- 可選:截?cái)嗖襟E3的輸出,將其用作布隆過濾器(Bloom filter)
- 在 SELECT 查詢中使用步驟3或4的輸出
- 解密結(jié)果疏咐。
在這個(gè)過程中的任何一步纤掸,你都可以根據(jù)實(shí)際使用情況進(jìn)行不同的權(quán)衡。
沒有 Side-Channels 的基于令牌的身份驗(yàn)證
深入: Split Tokens: Token-Based Authentication Protocols without Side-Channels
說到數(shù)據(jù)庫(上一節(jié))浑塞,你是否知道 SELECT 查詢理論上可能是定時(shí)信息泄漏的來源借跪?
簡單的緩解措施:
- 把你的認(rèn)證令牌分為兩半
- 一半在 SELECT 查詢中使用
- 后一半在恒定的時(shí)間(constant-time)驗(yàn)證
- 可以選擇將后半部分的散列存儲(chǔ)在數(shù)據(jù)庫中。這對(duì)于只能使用一次的令牌是有意義的酌壕,例如 密碼重置或“在此計(jì)算機(jī)上記住我”的令牌
即使可以使用定時(shí)泄漏來竊取一半的令牌垦梆,剩下的也需要暴力破解才能成功。
開發(fā)安全的API
我們寫了 SAPIENT (the Secure API ENgineering Toolkit)仅孩,讓服務(wù)器到服務(wù)器驗(yàn)證的消息傳遞變得簡單易行托猩。除了 HTTPS 提供的安全性之外,Sapient
允許你使用共享密鑰或公鑰來加密和驗(yàn)證消息辽慕。 這使得即使存在中間攻擊者京腥,并設(shè)有流氓證書頒發(fā)機(jī)構(gòu),你也可以使用Ed25519
對(duì) API 請(qǐng)求和響應(yīng)進(jìn)行身份驗(yàn)證溅蛉,或者將消息加密到只能由接收方服務(wù)器的密鑰解密的目標(biāo)服務(wù)器公浪。 由于每個(gè) HTTP 消息體都通過安全密碼進(jìn)行身份驗(yàn)證,所以可以安全地使用它來代替stateful token juggling protocols
(例如 OAuth)船侧。但是欠气,在密碼學(xué)方面,在做任何不規(guī)范的事情之前镜撩,總要確保他們的實(shí)現(xiàn)是由專家研究的预柒。
所有Sapient
使用的密碼算法都由Sodium cryptography library
提供。
進(jìn)一步閱讀:
Paragon Initiative Enterprises
已經(jīng)在其許多產(chǎn)品(包括許多開源軟件項(xiàng)目)中使用了Sapient
袁梗,
并將繼續(xù)添加軟件項(xiàng)目到Sapient
用戶群中宜鸯。
使用Chronicle記錄安全事件
深入: Chronicle Will Make You Question the Need for Blockchain Technology
Chronicle是一個(gè)基于散列鏈數(shù)據(jù)結(jié)構(gòu)的僅追加密碼分類賬(append-only cryptographic ledger),具有很多吸引公司“區(qū)塊鏈”技術(shù)的屬性遮怜,而不會(huì)過分矯枉過正淋袖。
除了僅追加密碼分類賬(append-only cryptographic ledger)這個(gè)具有創(chuàng)造性的用例之外,Chronicle
集成到SIEM中時(shí)锯梁,也可以十分有亮點(diǎn)即碗,因?yàn)槟憧梢詫踩P(guān)鍵事件發(fā)送到私人Chronicle
中焰情,并且它們是不能被改變的。
如果你的Chronicle
設(shè)置為將其摘要散列交叉簽名到其他Chronicle
實(shí)例剥懒,或者如果有其他實(shí)例配置為復(fù)制你的Chronicle
內(nèi)容烙样,攻擊者就很難篡改你的安全事件日志。
在Chronicle
的幫助下蕊肥,你可以獲得區(qū)塊鏈所承諾的彈性特性(resilience)谒获,而沒有任何隱私,性能或可伸縮性問題壁却。
要將數(shù)據(jù)發(fā)布到本地Chronicle
批狱,你可以使用任何與Sapient-compatible API,但最簡單的解決方案稱為Quill展东。
作者的一些話
一些聰明的讀者可能注意到我們引用了很多我們自己的工作赔硫,包括博客文章和開源軟件。(當(dāng)然也不僅僅引用了我們自己的工作)
這絕不是偶然的盐肃。
自從我們?cè)?2015 年初成立以來爪膊,一直在編寫安全庫并參與提高 PHP 生態(tài)系統(tǒng)安全性的工作。我們已經(jīng)涉足了很多領(lǐng)域砸王,而且我們的安全工程師(他們最近推動(dòng)了更安全的加密技術(shù)加入 PHP 核心推盛,就在最近的 PHP 7.2 中)自我擔(dān)保地說,并不擅長自我炒作谦铃,或是對(duì)已經(jīng)做過的工作持續(xù)熱情耘成。但你很可能沒有聽說我們多年來開發(fā)的工具或庫。對(duì)于這個(gè)驹闰,深感抱歉瘪菌。
不論如何,我們也不可能成為各方面的先行者嘹朗,所以我們盡可能地選擇與重視公共利益而不是貪圖小利的行業(yè)專家工作师妙。
這也是為什么瀏覽器安全的許多章節(jié)都參考了 Scott Helme 和他公司的工作,他們?cè)跒殚_發(fā)人員提供這些新的安全功能方面具有可訪問性和可理解性屹培。
本指南當(dāng)然不會(huì)是詳盡的默穴。編寫不安全代碼的方法幾乎和編寫代碼的方法一樣多。 安全是一種心態(tài)惫谤,而不是目的地壁顶。 隨著上面所寫的一切珠洗,以及后面涉及的資源溜歪,我們希望這將有助于全世界的開發(fā)人員,從今天開始用 PHP 編寫安全的軟件许蓖。
資源
如果你已經(jīng)按照本頁上的所有內(nèi)容進(jìn)行了操作蝴猪,并且需要更多內(nèi)容调衰,則可能會(huì)對(duì)我們策劃的閱讀列表感興趣,以便學(xué)習(xí)應(yīng)用程序安全性自阱。
如果你認(rèn)為自己編寫的代碼足夠安全嚎莉,并希望我們從安全工程師的角度對(duì)其進(jìn)行評(píng)判,這也是我們?yōu)榭蛻籼峁┑姆?wù)沛豌。
你如果為一家要進(jìn)行合規(guī)性測(cè)試(PCI-DSS趋箩,ISO 27001等)的公司工作,可能還想聘請(qǐng)我們公司來審核你的源代碼加派。我們的流程比其他安全咨詢公司更適合開發(fā)者叫确。
接下來是 PHP 和信息安全社區(qū)提供的資源列表,這些資源幫助互聯(lián)網(wǎng)更加安全芍锦。
PHP: The Right Way:現(xiàn)代 PHP 開發(fā)的實(shí)用指南竹勉,免費(fèi)在線。
Let's Encrypt:證書頒發(fā)機(jī)構(gòu)娄琉,通過提供免費(fèi) TLS 證書次乓,為創(chuàng)建更安全的 Internet 做了很多。
Qualys SSL Labs:為 TLS 配置提供了一個(gè)快速而簡單的測(cè)試套件孽水。幾乎每個(gè)人都使用這個(gè)來解決他們的密碼組和證書問題票腰,理由很充分:It does its job well.
Security Headers:可以檢驗(yàn)?zāi)愕木W(wǎng)站在使用瀏覽器安全功能來保護(hù)用戶方面的表現(xiàn)如何。
Report-URI:一個(gè)很好的免費(fèi)資源女气,提供監(jiān)控 CSP/HPKP 等安全策略的實(shí)時(shí)安全報(bào)告服務(wù)丧慈。他們給你一個(gè) Report-URI,你可以傳遞給你的用戶的瀏覽器主卫,如果有什么事情發(fā)生或有人發(fā)現(xiàn) XSS 攻擊媒介逃默,他們會(huì)投訴Report-URI。 Report-URI 會(huì)匯總這些錯(cuò)誤簇搅,并允許你更好地對(duì)這些報(bào)告進(jìn)行疑難解答和分類完域。
PHP Security Advent Calenda:RIPSTech旗下的團(tuán)隊(duì)負(fù)責(zé)。
Snuffleupagus:一個(gè)面向安全的 PHP 模塊(Suhosin的精神繼承者瘩将,似乎在很大程度上會(huì)被放棄)
PHP Delusions:一個(gè)致力于更好地使用 PHP 的網(wǎng)站吟税。大部分的口吻是非常有見地的,作者對(duì)技術(shù)的準(zhǔn)確性和清晰度的奉獻(xiàn)使得值得一讀姿现,特別是對(duì)于那些不太喜歡 PDO 功能的人來說肠仪。
Have I Been Pwned?:幫助用戶發(fā)現(xiàn)他們的數(shù)據(jù)是否屬于過時(shí)數(shù)據(jù)泄露。
結(jié)尾
原文地址:The 2018 Guide to Building Secure PHP Software - P.I.E. Staff