72個(gè)網(wǎng)絡(luò)應(yīng)用安全實(shí)操要點(diǎn),全方位保護(hù)你的Web應(yīng)用
本文轉(zhuǎn)載自微信公眾號(hào)「HelloGitHub」,作者Teo Selenius。轉(zhuǎn)載本文請(qǐng)聯(lián)系HelloGitHub公眾號(hào)。
對(duì)于開(kāi)發(fā)者而言,網(wǎng)絡(luò)安全的重要性不言而喻。任何一處代碼錯(cuò)誤、一個(gè)依賴(lài)項(xiàng)漏洞或是數(shù)據(jù)庫(kù)的端口暴露到公網(wǎng),都會(huì)有可能直接送你上熱搜。
那么,哪里可以找到詳細(xì)的避雷指引呢?OWASP's top 10 清單太短了,而且它更關(guān)注的是漏洞羅列,而非對(duì)預(yù)防。相比之下,ASVS 是個(gè)很好的列表,但還是滿足不了實(shí)際需求。
本文這份清單將介紹 72 個(gè)實(shí)操要點(diǎn),讓你全方位保護(hù)你的 Web 應(yīng)用程序。各位看官,準(zhǔn)備入坑啦!
一、瀏覽器端的威脅防御
1、用且僅用 HTTPS,防范網(wǎng)絡(luò)攻擊
眾所周知,一個(gè)安全的應(yīng)用需要對(duì)瀏覽器和 Web 服務(wù)器之間的所有連接進(jìn)行加密。此外,建議禁用一些舊的密碼套件和協(xié)議。使用 HTTPS 時(shí),僅加密網(wǎng)站的“敏感”部分是不夠的。如非這樣,攻擊者可以截獲某個(gè)未加密的 HTTP 請(qǐng)求,然后偽造來(lái)自服務(wù)器的響應(yīng),返回惡意內(nèi)容。幸運(yùn)的是,HTTPS 目前是很容易做到的。我們可以通過(guò) Let's Encrypt 免費(fèi)獲得證書(shū),加上 CertBot 免費(fèi)續(xù)期。
繼續(xù)我們的清單,下一個(gè)是 HSTS 它與 HTTPS 密切相關(guān)。
2、使用 HSTS 和預(yù)加載來(lái)保護(hù)用戶免受 SSL 剝離攻擊
服務(wù)器可以用 HSTS 或 Strict Transport Security header 來(lái)強(qiáng)制進(jìn)行加密連接。它表示需要一直使用 HTTPS 連接訪問(wèn)網(wǎng)站。
HSTS 可以防止 SSL 剝離攻擊。所謂的 SSL 剝離攻擊也就是:網(wǎng)絡(luò)上的攻擊者截獲瀏覽器發(fā)出的第一個(gè) HTTP 請(qǐng)求(通常是未加密的),并立即偽造對(duì)該請(qǐng)求的回復(fù),假裝是服務(wù)器并將連接降級(jí)為明文 HTTP。
值得注意的是,HSTS 僅在用戶至少成功訪問(wèn)了一次應(yīng)用程序的情況下才能生效。為了克服這個(gè)限制,可以把我們的網(wǎng)站提交到 https://hstspreload.org ,這樣,各瀏覽器便可以將我們的域名通過(guò)硬編碼寫(xiě)入到 HSTS 列表中。
如下:
- Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
警告:
在實(shí)施 HSTS 時(shí),將會(huì)強(qiáng)制進(jìn)出該網(wǎng)站的所有網(wǎng)絡(luò)請(qǐng)求均被加密,如果網(wǎng)站請(qǐng)求中仍然有純文本,可能無(wú)法訪問(wèn)。所以,先設(shè)置一個(gè)小的 max-age 參數(shù)進(jìn)行調(diào)試,如果一切正常工作,再加大這個(gè)值。調(diào)試成功后再加上預(yù)加載 (preload) ,把開(kāi)啟預(yù)加載保留在最后一步,因?yàn)殛P(guān)閉它是件很麻煩和痛苦的事情。
3、設(shè)置安全 Cookie,保護(hù)用戶免受網(wǎng)絡(luò)攻擊
給 Cookie 加上 Secure 屬性。此屬性將防止 Cookie 在(意外或強(qiáng)制的)未加密的連接中泄漏。
Set-Cookie: foo=bar; ...other options... Secure
4、安全生成 HTML 以避免 XSS 漏洞
要避免 XSS(跨站點(diǎn)腳本)漏洞,可以采用下面兩種方法:
- 完全靜態(tài)的網(wǎng)站(例如 JavaScript SPA + 后端API)。避免生成 HTML 問(wèn)題的最有效方法是根本不生成HTML,如前述方法,當(dāng)然,也可以試試很酷的 NexJS。
- 模板引擎。針對(duì)傳統(tǒng)的 Web 應(yīng)用程序,其中的 HTML 大多是在后端服務(wù)器上根據(jù)提供參數(shù)動(dòng)態(tài)生成的。這種情況下,不要通過(guò)字符串連接來(lái)創(chuàng)建 HTML 。推薦的做法是使用模板引擎,比如 PHP 語(yǔ)言的 Twig、Java 語(yǔ)言的 Thymeleaf、Python 語(yǔ)言的 Jinja2 等等。
此外,務(wù)必要正確配置模板引擎,從而可以自動(dòng)對(duì)參數(shù)進(jìn)行編碼,并且不要使用任何可以繞過(guò)這種編碼的“不安全”函數(shù)。不要把 HTML 放在回調(diào)函數(shù)、屬性(不帶引號(hào))或 href/src 等諸如此類(lèi)的地方。
5、安全使用 JavaScript 以避免 XSS 漏洞
要避免 JavaScript 端的 XSS(跨站點(diǎn)腳本)漏洞,切忌將不受信任的數(shù)據(jù)傳遞到可執(zhí)行代碼的函數(shù)或?qū)傩灾?。這類(lèi)常見(jiàn)的函數(shù)或?qū)傩园ǎ?/p>
- eval、setTimeout、setInterval 等。
- innerHTML,React's dangerouslySetInnerHTML 等。
- onClick、onMouseEnter、onError 等。
- href、src 等。
- location, location.href 等。
6、沙箱處理不可信內(nèi)容,避免 XSS 漏洞
最好是能避免不可信的內(nèi)容,但往往又不能完全避免:例如需要從遠(yuǎn)程獲取 HTML 進(jìn)行展示,或者需要允許用戶用所見(jiàn)即所得的編輯器寫(xiě)文章,等等。
要避免這些場(chǎng)景中的 XSS(跨站點(diǎn)腳本)漏洞,請(qǐng)首先使用 DOMPurify 清理內(nèi)容,然后在沙箱中進(jìn)行內(nèi)容呈現(xiàn)。
即使所見(jiàn)即所得的編輯庫(kù)聲稱(chēng)從 HTML 中移除了惡意內(nèi)容,仍然可以通過(guò)重新凈化和沙箱來(lái)處理,進(jìn)一步確保安全。
還有一種常見(jiàn)的情況是,我們想在網(wǎng)頁(yè)展示廣告等內(nèi)容。這種情況下簡(jiǎn)單采用 IFrame 是不夠的,因?yàn)?same-origin 策略會(huì)允許跨域的 frame 將父級(jí) frame (也就是我們的網(wǎng)站)的 URL 修改為一個(gè)釣魚(yú)網(wǎng)站。因此,要記住使用 IFrame 的沙箱屬性來(lái)避免此種情況的發(fā)生。
7、采用內(nèi)容安全策略,避免 XSS 漏洞
內(nèi)容安全策略(CSP)可以很好地防御 XSS(跨站點(diǎn)腳本)攻擊、點(diǎn)擊劫持攻擊等。所以,一定要用它!默認(rèn)情況下,CSP 會(huì)阻止幾乎所有的危險(xiǎn)操作,所以額外的配置越少越好。如下:
- Content-Security-Policy: default-src 'self'; form-action 'self'; object-src 'none'
它允許從 Web 應(yīng)用程序的源代碼加載腳本、樣式、圖像、字體等,但不允許加載其他內(nèi)容。最值得注意的是,它將阻止內(nèi)聯(lián)腳本()的運(yùn)行,從而更好地預(yù)防 XSS 漏洞。
此外,form-action:'self' 指令可防止在網(wǎng)站上創(chuàng)建惡意 HTML 表單(比如“您的會(huì)話已過(guò)期,請(qǐng)?jiān)诖颂庉斎朊艽a”類(lèi)似的表單),并將其提交到攻擊者的服務(wù)器。
無(wú)論如何,都不要指定 script-src: unsafe inline ,一旦這樣做,CSP 將形同虛設(shè)。
最后,如果你擔(dān)心 CSP 會(huì)影響生產(chǎn)環(huán)境,可以先以 Report-Only 模式進(jìn)行部署:
- Content-Security-Policy-Report-Only: default-src 'self'; form-action 'self'
8、設(shè)置 HttpOnly 的 Cookie,保護(hù)用戶免受 XSS 攻擊
為 Cookie 設(shè)置 HttpOnly 屬性,可以防止 Cookie 被 JavaScript 代碼訪問(wèn)。一旦跨腳本攻擊發(fā)生,該設(shè)置也會(huì)讓黑客更難竊取到 Cookie 信息。當(dāng)然,有些需要被 JavaScript 代碼訪問(wèn)的 Cookie,就不能做這個(gè)設(shè)置了。
- Set-Cookie: foo=bar; ...other options... HttpOnly
9、針對(duì)下載功能,合理設(shè)置避免 XSS 漏洞
向用戶提供下載功能時(shí),在 header 中設(shè)置 Content-Disposition: attachment,從而避免 XSS 漏洞。該設(shè)置將禁止在用戶瀏覽器直接渲染文件,從而避免 HTML 或 SVG 格式的下載文件可能引發(fā)的漏洞。如下:
- Content-Disposition: attachment; filename="document.pdf"
假如我們想允許特定的文件(如 pdf)能在瀏覽器端打開(kāi),并且也確定這樣是安全的,那么,可以針對(duì)該類(lèi)型文件,將 header 省略掉或是將 attachment 換為 inline。
10、針對(duì) API 響應(yīng),合理設(shè)置避免 XSS 漏洞
反射型文件下載(RFD)攻擊往往通過(guò)構(gòu)建一個(gè) URL 從 API 下載一個(gè)惡意文件來(lái)實(shí)現(xiàn)。針對(duì)該類(lèi)漏洞,可采用在 API HTTP 響應(yīng)中返回帶有安全文件名的 Content-Disposition header來(lái)防御。
11、利用現(xiàn)有平臺(tái)的反跨站請(qǐng)求偽造(CSRF)機(jī)制,避免 CSRF 漏洞
為避免反跨站請(qǐng)求偽造漏洞,務(wù)必確保我們所采用的平臺(tái)開(kāi)啟了反跨站請(qǐng)求偽造功能,并確保該配置發(fā)揮了應(yīng)有的作用。
12、驗(yàn)證 OAuth 身份認(rèn)證的 state 參數(shù),避免 CSRF 漏洞
有一類(lèi)與 OAuth 身份認(rèn)證相關(guān)的跨站請(qǐng)求偽造漏洞是黑客讓用戶不經(jīng)意間采用其賬戶進(jìn)行登錄。因此,如果有使用 OAuth 身份認(rèn)證,務(wù)必確保對(duì)狀態(tài)(state)參數(shù)的驗(yàn)證。
13、正確使用 HTTP 協(xié)議,避免 CSRF 漏洞
除了 POST、PUT、PATCH、DELETE 以外,不要使用其它 HTTP 方法進(jìn)行數(shù)據(jù)更改。GET 請(qǐng)求一般是不包含在反跨站請(qǐng)求偽造機(jī)制中的。
14、為 Cookie 設(shè)置同源屬性,避免 CSRF、XS-leak、XSS 漏洞
為 Cookie 設(shè)置 SameSite 屬性。SameSite 能防止大多數(shù)的跨站點(diǎn)請(qǐng)求偽造攻擊,而且還可以防止許多跨站點(diǎn)泄漏的漏洞。
SameSite 屬性有兩種模式:寬松(lax)和嚴(yán)格(strict)。
寬松模式可以防止大多數(shù)跨站點(diǎn)計(jì)時(shí)和跨站點(diǎn)請(qǐng)求偽造攻擊,但對(duì)基于 Get 請(qǐng)求的跨站點(diǎn)請(qǐng)求偽造漏洞無(wú)效。如下:
- Set-Cookie: foo=bar; ...other options... SameSite=Lax
嚴(yán)格模式則可以防止該類(lèi)基于 Get 請(qǐng)求的漏洞,以及反射型的跨站點(diǎn)腳本漏洞。然而,嚴(yán)格模式不適合常規(guī)的應(yīng)用程序,因?yàn)樗鼤?huì)中斷身份驗(yàn)證鏈接。如果用戶已登錄某個(gè)網(wǎng)站,現(xiàn)在要在新的頁(yè)面打開(kāi)指向該應(yīng)用程序的鏈接,則打開(kāi)的新頁(yè)面將不會(huì)為該用戶自動(dòng)登錄。由于嚴(yán)格模式的限制,會(huì)話 Cookie 也不會(huì)隨請(qǐng)求一起發(fā)送。嚴(yán)格模式設(shè)置如下:
- Set-Cookie: foo=bar; ...other options... SameSite=Strict
15、每次登錄創(chuàng)建一個(gè)新的會(huì)話 ID,防止會(huì)話固定攻擊
會(huì)話固定攻擊一般是在以下情形發(fā)生:
- 攻擊者將 Cookie(例如 JSESSIONID=ABC123)注入到用戶的瀏覽器中。
- 用戶使用其憑據(jù)登錄,并在登錄請(qǐng)求中提交攻擊者設(shè)置的 JSESSIONID=ABC123 。
- 應(yīng)用程序?qū)?Cookie 和用戶進(jìn)行身份驗(yàn)證。
- 與此同時(shí),擁有該 Cookie 的攻擊者也就可以通過(guò)該用戶的身份進(jìn)行登錄了。
為了防止出現(xiàn)這種情況,程序中需要在身份驗(yàn)證通過(guò)后,創(chuàng)建一個(gè)新的會(huì)話 ID 返回給用戶,而不是驗(yàn)證可能被動(dòng)了手腳的 Cookie。
16、合理命名 Cookie,防止會(huì)話固定攻擊
難道 Cookie 命名也能影響到網(wǎng)絡(luò)應(yīng)用程序的安全性?確實(shí)如此!將 Cookie 采用 __Host-** 的形式來(lái)命名,瀏覽器將:
- 不能通過(guò)非加密的鏈接訪問(wèn)該項(xiàng) Cookie, 從而避免會(huì)話固定攻擊以及其它涉及到 Cookie 讀取與寫(xiě)入的攻擊;
- 不允許子域名重寫(xiě)該項(xiàng) Cookie,從而避免來(lái)自子域名網(wǎng)站(抑或是被攻陷,抑或本身就是惡意的)的攻擊。
該項(xiàng)設(shè)置示例如下:
- Set-Cookie: __Host-foo=bar ...other options...
17、設(shè)置 Cache-Control header,防止用戶信息被竊取
緩存是將訪問(wèn)過(guò)的網(wǎng)站、下載過(guò)的文件全部存儲(chǔ)在硬盤(pán)的某個(gè)位置,直到有人手動(dòng)刪除它們。默認(rèn)情況下,瀏覽器會(huì)對(duì)頁(yè)面的一切內(nèi)容進(jìn)行緩存,從而加快訪問(wèn)速度、節(jié)約網(wǎng)絡(luò)帶寬。
要在公共網(wǎng)絡(luò)環(huán)境保證信息安全,我們需要將所有 HTTP 響應(yīng)設(shè)置一個(gè)合適的 Cache-Control header,特別是針對(duì)非公開(kāi)的和動(dòng)態(tài)的內(nèi)容。
該項(xiàng)設(shè)置示例如下:
- Cache-Control: no-store, max-age=0
18、設(shè)置 Clear-Site-Data header,防止用戶信息被竊取
另外一個(gè)可以有效保證用戶退出后記錄即被清除的 header 是 Clear-Site-Data 。當(dāng)用戶退出登錄時(shí),可以在 HTTP 請(qǐng)求中攜帶該 header。瀏覽器會(huì)清除該域名下的緩存、Cookie、存儲(chǔ)以及執(zhí)行上下文。大部分瀏覽器都支持該 header。
該項(xiàng)設(shè)置示例如下:
- Clear-Site-Data: "*"
19、妥當(dāng)?shù)靥幚?ldquo;退出”,防止用戶信息被竊取
用戶退出登錄后,務(wù)必要對(duì)訪問(wèn)令牌和會(huì)話識(shí)別碼進(jìn)行失效處理。這樣,即使攻擊者從訪問(wèn)歷史/緩存/內(nèi)存等地方獲取到這些信息,它們也不再有效。
此外,如果有單點(diǎn)登錄,切記要調(diào)用單點(diǎn)登錄的退出端口。否則,因?yàn)閱吸c(diǎn)登錄會(huì)話仍處于活躍狀態(tài),此時(shí)的退出將會(huì)無(wú)效,只要用戶再次點(diǎn)擊“登錄”,即可自動(dòng)登錄。
最后,清理掉你可能用到過(guò)的 Cookie、HTML5 存儲(chǔ)等。上面提到的 Clear-Site-Data 還未被某些瀏覽器支持,所以最好還是手工清除一下。
20、針對(duì) JavaScript 密碼采用 SessionStorage,防止用戶信息被后來(lái)者竊取
SessionStorage 類(lèi)似于 LocalStorage,但對(duì)每個(gè)標(biāo)簽頁(yè)都是獨(dú)有的,而且在瀏覽器/標(biāo)簽頁(yè)關(guān)閉以后將自動(dòng)清除。
注意:如果要在系統(tǒng)內(nèi)打開(kāi)的多個(gè)標(biāo)簽頁(yè)之間同步用戶的授權(quán)信息,那就需要用事件來(lái)同步 sessionStorage 信息。
21、不要通過(guò) URL 傳輸敏感信息
URL 設(shè)計(jì)的初衷就不是為了傳輸敏感信息。它會(huì)被顯示在屏幕上,存儲(chǔ)到瀏覽器歷史記錄,也容易隨 referrer header 而泄漏,被記錄在服務(wù)器日志等。所以,切忌在 URL 中傳遞敏感信息。
22、采用 Referrer 策略,防止 URL 地址泄露
默認(rèn)情況下,當(dāng)從系統(tǒng)中鏈接到一個(gè)外部網(wǎng)站時(shí),瀏覽器會(huì)設(shè)置一個(gè) Referrer 的 header 來(lái)告訴該網(wǎng)站此次訪問(wèn)的來(lái)源。這個(gè) header 包含了整個(gè) URL 地址,這可能就涉及到一點(diǎn)隱私。
可以在 HTTP 響應(yīng)中設(shè)置一個(gè) Referrer-Policy 的 header 來(lái)禁止該默認(rèn)行為:
- Referrer-Policy: no-referrer
23、為應(yīng)用設(shè)置獨(dú)立域名,防止同源應(yīng)用相互干擾
如果我們這樣設(shè)置應(yīng)用域名:https://www.example.com/app1/ 和 https://www.example.com/app2/,是非常危險(xiǎn)的。因?yàn)闉g覽器會(huì)認(rèn)為它們是同源應(yīng)用,也就是同樣的服務(wù)主機(jī)、端口和模式。正因?yàn)槭峭磻?yīng)用,它們將對(duì)彼此有完全的訪問(wèn)權(quán)限。任何影響其中一個(gè)的漏洞都會(huì)同樣影響到另外一個(gè)。
因此,我們需要給每個(gè)應(yīng)用一個(gè)獨(dú)立的域名。所以,這種情況下應(yīng)該設(shè)置為:https://app1.example.com/ 和 https://app2.example.com/
注意:位于同一個(gè)域名下的子域名是可以為整個(gè)域名設(shè)置 Cookie 的。例如 app1.example.com 可以為 example.com 設(shè)置 Cookie,而這個(gè) Cookie 也將適用于 app2.example.com。允許為一個(gè)站點(diǎn)設(shè)置 Cookie 有時(shí)會(huì)給會(huì)話固定等類(lèi)型的漏洞以可乘之機(jī)。公共后綴列表可以用來(lái)應(yīng)對(duì)該問(wèn)題。此外,也可以通過(guò)將 Cookie 命名為 __Host- 來(lái)防止其被子域名所覆蓋。
24、謹(jǐn)慎采用 CORS(跨域資源共享)
瀏覽器的安全模型大部分是依賴(lài)于同源策略,它可以防止應(yīng)用的跨域讀取。而 CORS(跨域資源共享)則是一種允許網(wǎng)站進(jìn)行跨域資源訪問(wèn)的手段。所以,決定使用它之前,最好先搞清楚自己是否真的需要。
25、限制請(qǐng)求來(lái)源
如果你在 api.example.com 的服務(wù)需要被來(lái)自 www.example.com 的 GET 請(qǐng)求訪問(wèn),那么可以在 api.example.com 服務(wù)上指定如下header:
- Access-Control-Allow-Origin: https://www.example.com
如果你有個(gè)公開(kāi)的服務(wù)接口(比如說(shuō)一個(gè)提供給互聯(lián)網(wǎng)上 JavaScript 客戶端使用的計(jì)算器服務(wù)),那么你可以指定一個(gè)隨機(jī)的來(lái)源:
- Access-Control-Allow-Origin: *
如果你只想讓有限的幾個(gè)域名訪問(wèn)它,那么可以在程序中讀取請(qǐng)求的 Origin header,進(jìn)行比對(duì)后處理。不過(guò),建議使用現(xiàn)成的庫(kù)來(lái)操作,不要徒手?jǐn)],很容易出錯(cuò)。
26、謹(jǐn)慎使用 allow credentials 選項(xiàng)
默認(rèn)情況下,跨域資源共享是不帶用戶憑證的。但如果在 Web 服務(wù)器端指定如下 header,則將允許攜帶:
- Access-Control-Allow-Origin: https://www.example.com
- Access-Control-Allow-Credentials: true
這對(duì) header 組合相當(dāng)危險(xiǎn)。因?yàn)樗鼤?huì)使跨域訪問(wèn)具備已登錄用戶的權(quán)限,并使用該權(quán)限來(lái)訪問(wèn)網(wǎng)站資源。所以,如果你不得不使用它,務(wù)必小心為上。
27、對(duì) HTTP method 進(jìn)行驗(yàn)證
僅允許所需要的 HTTP 方法,從而最小化攻擊面。
- Access-Control-Allow-Methods: GET
28、合理使用 WebSockets, 避免反跨站請(qǐng)求偽造等漏洞
WebSockets 迄今還是比較新的技術(shù),技術(shù)文檔較少使用它難免會(huì)有些風(fēng)險(xiǎn)。所以,采用時(shí)務(wù)必要做到以下幾點(diǎn):
- 對(duì)連接進(jìn)行加密
就像我們應(yīng)該用 https:// 而非 http:// 采用 WebSockets 時(shí)也要使用 wss:// 而非 ws://
HSTS 也會(huì)影響 WebSockets ,它會(huì)自動(dòng)將非加密的 WebSocket 連接升級(jí)到 wss://
- 對(duì)連接進(jìn)行鑒權(quán)
如果使用的是基于 Cookie 的鑒權(quán)機(jī)制,且 WebSocket 服務(wù)器與應(yīng)用服務(wù)器在同一個(gè)域名下,那就可以在 WebSocket 中繼續(xù)使用已有的會(huì)話。不過(guò)切記要對(duì)請(qǐng)求源進(jìn)行驗(yàn)證!
如果不是基于 Cookie ,可以在系統(tǒng)中創(chuàng)建一個(gè)單次使用、有時(shí)間限制并與用戶 IP 綁定的授權(quán)令牌,用該令牌對(duì) WebSocket 進(jìn)行授權(quán)。
- 對(duì)連接源進(jìn)行確認(rèn)
理解 WebSockets 的一個(gè)關(guān)鍵點(diǎn)在于要知道同源策略對(duì)其是無(wú)效的。任何一個(gè)能與你的系統(tǒng)建立 WebSocket 連接的網(wǎng)站,在使用 Cookie 鑒權(quán)的時(shí)候,都是可以直接獲得用戶信息的。因此,在 WebSocket 握手時(shí),必須要確認(rèn)連接源??梢酝ㄟ^(guò)驗(yàn)證請(qǐng)求頭中的 Origin 參數(shù)來(lái)確認(rèn)。
如果想要做到雙重保險(xiǎn),可以采用反跨站請(qǐng)求偽造令牌作為 URL 參數(shù)。但針對(duì)每個(gè)任務(wù)則需要?jiǎng)?chuàng)建一次性的獨(dú)立令牌,而不要直接使用反跨站請(qǐng)求偽造令牌,因?yàn)楹笳咧饕怯脕?lái)為應(yīng)用的其它部分提供安全保障的。
29、采用 U2F 令牌或客戶端證書(shū),保護(hù)系統(tǒng)關(guān)鍵用戶免受釣魚(yú)攻擊
如果系統(tǒng)可能會(huì)面臨釣魚(yú)攻擊的威脅,說(shuō)人話也就是,“如果存在這樣的可能性:攻擊者創(chuàng)建一個(gè)假的網(wǎng)站,騙取管理員/CEO 或其它用戶的信任,從而盜取其用戶名、密碼和驗(yàn)證碼”,那么就應(yīng)該使用 U2F 令牌或客戶端證書(shū)來(lái)防止這種攻擊,這樣的話即使攻擊者有了用戶名、密碼和驗(yàn)證碼也無(wú)法得逞。
備注:強(qiáng)調(diào)釣魚(yú)防護(hù)對(duì)于一般用戶而言往往會(huì)帶來(lái)不必要的麻煩。然而,提供多一種可選項(xiàng)對(duì)終端用戶而言也非壞事。此外,向用戶提前告知釣魚(yú)攻擊的危險(xiǎn)也是非常必要的。
30、針對(duì)跨站點(diǎn)泄露進(jìn)行保護(hù)
跨站點(diǎn)泄露是一系列瀏覽器邊信道攻擊。這種攻擊使惡意網(wǎng)站可以從其它 Web 應(yīng)用程序的用戶中推測(cè)出信息。
這種攻擊存在已有時(shí)日,但是瀏覽器端卻是最近才開(kāi)始添加針對(duì)性的預(yù)防機(jī)制??梢栽?這篇文章 中了解關(guān)于該類(lèi)攻擊的更多細(xì)節(jié)以及應(yīng)該采取的安全控制措施。
二、服務(wù)器端的威脅防御
其次,是服務(wù)器端的威脅防御,這里從應(yīng)用系統(tǒng)、基礎(chǔ)設(shè)施、應(yīng)用架構(gòu)、應(yīng)用監(jiān)控、事件響應(yīng)等不同側(cè)面,歸納了如下建議:
2.1 應(yīng)用系統(tǒng)
31、對(duì)用戶輸入進(jìn)行合法性驗(yàn)證
該類(lèi)別的措施中最關(guān)鍵的一點(diǎn)就是盡可能?chē)?yán)格地對(duì)所有用戶輸入進(jìn)行合法性驗(yàn)證。適當(dāng)?shù)尿?yàn)證會(huì)使系統(tǒng)漏洞更難被發(fā)現(xiàn)和利用。對(duì)不合法的用戶輸入直接拒絕,而不要嘗試去清洗。驗(yàn)證方面包括如下:
- 采用嚴(yán)格的數(shù)據(jù)類(lèi)型。針對(duì)日期采用 DataTime 類(lèi)型,數(shù)字采用 Integer 類(lèi)型等等。針對(duì)有固定可選項(xiàng)的情況采用枚舉類(lèi)型。盡量避免采用字符串類(lèi)型。
- 如果必須采用字符串,至少給一個(gè)長(zhǎng)度限制。
- 如果必須采用字符串,將可輸入的字符集盡可能地減少。
- 如果要處理 JSON,使用 JSON 模式進(jìn)行驗(yàn)證。
- 如果要處理 XML,使用 XML 模式進(jìn)行驗(yàn)證。
32、異常處理優(yōu)雅化,避免技術(shù)細(xì)節(jié)泄露
對(duì)終端用戶不要顯示堆棧記錄或類(lèi)似的調(diào)試信息。采用全局的異常處理器對(duì)異常進(jìn)行處理,展現(xiàn)給瀏覽器端簡(jiǎn)單的錯(cuò)誤信息。這樣會(huì)使攻擊者更難發(fā)現(xiàn)和利用系統(tǒng)中的漏洞。
33、不要自己做鑒權(quán)
對(duì)用戶進(jìn)行鑒權(quán)時(shí)可能會(huì)出現(xiàn)各種各樣的問(wèn)題:要抵御密碼猜想攻擊、用戶枚舉攻擊,要管理密碼重置、存儲(chǔ)用戶憑證,樣樣都不容易。就像密碼處理一樣復(fù)雜,我們普通人還是不要嘗試了。
直接使用 auth0 等類(lèi)似的工具來(lái)進(jìn)行身份驗(yàn)證,采用一些廣泛使用的、安全的軟件模塊來(lái)實(shí)現(xiàn)通信協(xié)議(常見(jiàn)的為 OpenID connect)。如果不想用 auth0 這類(lèi)第三方的身份提供商,也可以自己搭建一個(gè)類(lèi)似 KeyCloak 的服務(wù)來(lái)代替。
34、對(duì)一切都進(jìn)行鑒權(quán),減少攻擊面
應(yīng)用系統(tǒng)要默認(rèn)對(duì)一切都進(jìn)行鑒權(quán),除非是一些靜態(tài)資源、異常頁(yè)面或登出頁(yè)面。
35、采用多重身份認(rèn)證
萬(wàn)一有人破解了身份認(rèn)證服務(wù)呢?如果存在這種擔(dān)憂,直接上多重身份認(rèn)證(說(shuō)人話也就是:除了密碼以外,還需要手機(jī)驗(yàn)證碼)。這樣就算身份認(rèn)證服務(wù)被黑、攻擊者可以冒充到任何人,還是無(wú)法知道手機(jī)收到的驗(yàn)證碼。
36、通過(guò)嚴(yán)格的權(quán)限控制,避免對(duì)數(shù)據(jù)或功能的未授權(quán)訪問(wèn)
權(quán)限控制雖不是件容易事,但也有妥善處理的方法:只要時(shí)刻記住不要在控制器方法中忘了對(duì)用戶權(quán)限進(jìn)行驗(yàn)證,從而帶來(lái)用戶越權(quán)的漏洞,包括:
- 不要默認(rèn)對(duì)所有控制器方法開(kāi)通訪問(wèn)權(quán)限。
- 根據(jù)用戶角色劃分每個(gè)控制器的訪問(wèn)權(quán)限。
- 采用方法級(jí)別的安全控制,限制對(duì)服務(wù)方法的訪問(wèn)權(quán)限。
- 采用集中化的權(quán)限管理工具,防止對(duì)每條記錄的非授權(quán)訪問(wèn)。
- 采用前端 Web 應(yīng)用和后臺(tái) API 結(jié)合的架構(gòu),對(duì)每個(gè) App 和 API 均采取權(quán)限控制,而不僅是對(duì)與互聯(lián)網(wǎng)連接的部分進(jìn)行控制。
- 為了進(jìn)一步澄清權(quán)限管理工具,這里總結(jié)了一些要點(diǎn):
- 數(shù)據(jù)記錄要有可以進(jìn)行權(quán)限控制的字段,比如 int ownerId。
- 被授權(quán)的用戶要有一個(gè) ID。
- 要有一個(gè)類(lèi)可用來(lái)進(jìn)行權(quán)限評(píng)估,在數(shù)據(jù)記錄的 ownerId 與 用戶的 ID 相匹配時(shí),能判斷出用戶具有對(duì)應(yīng)的訪問(wèn)權(quán)限。
- 在以上基礎(chǔ)上,可以將權(quán)限評(píng)估類(lèi)集成到應(yīng)用平臺(tái)的權(quán)限控制系統(tǒng)中,比如 Spring Security 產(chǎn)品的 PreAuthorize、PostAuthorize 等等。
- 如果需要更復(fù)雜的權(quán)限控制,也可以搭建一個(gè)完善的 ACL 系統(tǒng)。
37、采用合適的工具和技術(shù),避免注入漏洞
注入類(lèi)的漏洞有很多,而且都很相似,包括 SQL 注入、HTML 注入、XML 注入、XPath 注入、命令注入、SMTP 注入、響應(yīng) header 注入等等。名稱(chēng)不同但本質(zhì)相同,相應(yīng)地解決方法也類(lèi)似:
- 問(wèn)題原因:使用字符串拼接,來(lái)構(gòu)建特定協(xié)議下的參數(shù)化消息。
- 解決方案:采用合適的、安全的、現(xiàn)成的工具來(lái)實(shí)現(xiàn)這項(xiàng)任務(wù)。
這里不會(huì)深入太多細(xì)節(jié),只要記住:不管你是什么協(xié)議,都謹(jǐn)記上面這點(diǎn)。后面會(huì)列舉一些常見(jiàn)的注入類(lèi)漏洞。
38、創(chuàng)建安全的數(shù)據(jù)庫(kù)查詢語(yǔ)句,避免 SQL 注入漏洞
如果要避免 SQL 注入漏洞,那就記住絕不要自己用字符串拼接 SQL 查詢語(yǔ)句。采用一個(gè)對(duì)象關(guān)系映射框架(ORM)來(lái)實(shí)現(xiàn),可以讓開(kāi)發(fā)更高效、應(yīng)用更安全。
如果想要構(gòu)建更細(xì)粒度的查詢,可以使用更底層一點(diǎn)的 ORM。
如果不能使用 ORM,那就嘗試預(yù)處理語(yǔ)句,但也要小心這類(lèi)語(yǔ)句會(huì)比 ORM 更容易出現(xiàn)錯(cuò)誤。
警告:
ORM 框架也不是萬(wàn)能的,體現(xiàn)在兩方面:一是,它對(duì)原生的 SQL 查詢還是支持的,最好不要使用這類(lèi)查詢;二是,像其它任何軟件一樣,ORM 框架也會(huì)時(shí)不時(shí)被曝出漏洞。所以,還是遵循我們一而再再而三強(qiáng)調(diào)的策略:對(duì)所有輸入進(jìn)行驗(yàn)證,采用網(wǎng)絡(luò)應(yīng)用程序防火墻(WAF),并保持軟件包的更新,這樣基本就可以放心了。
39、謹(jǐn)慎使用操作系統(tǒng)的命令行,防止命令注入的相關(guān)漏洞
如果可以避免,最好不要執(zhí)行操作系統(tǒng)命令。如果不能避免,那最好遵循以下準(zhǔn)則:
- 采用合適的庫(kù)/方法來(lái)構(gòu)建命令及其參數(shù)。參數(shù)必須是 list 類(lèi)型。不要用單獨(dú)字符串來(lái)創(chuàng)建命令。
- 不用使用 shell 來(lái)調(diào)用命令。
- 預(yù)定義好命令參數(shù)。比如 curl,如果允許用戶通過(guò) -o 來(lái)指定參數(shù),那么攻擊者就有機(jī)會(huì)寫(xiě)入到本地文件系統(tǒng)。
- 了解程序如何執(zhí)行,并相應(yīng)地對(duì)參數(shù)進(jìn)行驗(yàn)證。再比如 curl,你可能只是想讓用戶可以拉取某個(gè)網(wǎng)站的內(nèi)容,但如果他拉取了 file:///etc/passwd,那就危險(xiǎn)了。
- 想清楚再行動(dòng)。在上面的例子中,就算驗(yàn)證了訪問(wèn)地址是以 http:// 或 https:// 開(kāi)頭,攻擊者也可以發(fā)起以這兩類(lèi)協(xié)議開(kāi)頭的攻擊,如:http://192.168.0.1/internal_sensitive_service/admin。
- 再?gòu)?qiáng)調(diào)一遍:真得要想清楚了再行動(dòng)。就算你對(duì) DNS 進(jìn)行驗(yàn)證,確保命令中不含敏感內(nèi)網(wǎng)地址,你有去禁止將特定 DNS 記錄映射到 192.168.0.1 嗎?如果答案是否,那就危險(xiǎn)了。
40、合理配置 XML 解析器,避免 XML 漏洞
作為一種標(biāo)記語(yǔ)言,XML 的危險(xiǎn)性體現(xiàn)在它可以訪問(wèn)系統(tǒng)資源。XSLT 的一些實(shí)現(xiàn)甚至支持嵌入代碼。因此,在處理時(shí)必須非常謹(jǐn)慎。
- 如果可以,避免接受來(lái)自不受信任源的 XML/XSLT。
- 如果要向 XML、XSLT 或 XPath 傳參,記住要使用安全的軟件組件,而不要使用字符串連接/格式化的方式。
- 使用主流、安全的軟件組件來(lái)解析 XML/XSLT。不要使用錯(cuò)誤的庫(kù)或代碼來(lái)處理 XML。此外,在任何情況下,都不要試圖去徒手?jǐn)]一個(gè)解析器(比如 SAML),非常容易出錯(cuò)。
- 正確配置解析器:禁用 XSLT 文檔、禁用 xinclude、禁用文檔類(lèi)型定義、禁用外部實(shí)體,啟用 DOS 保護(hù)。具體配置在實(shí)現(xiàn)時(shí)會(huì)有所不同,但務(wù)必對(duì)所選擇的解析器進(jìn)行深入的研究。
41、采用合適的類(lèi)構(gòu)建URL,避免 URL 注入漏洞
URL 注入經(jīng)常會(huì)在以下情況發(fā)生:
- flavour = request.getParam("flavour");
- url = "https:/api.local/pizzas/" + flavour + "/";
- return get(url).json();
如果 flavour 被設(shè)置為:
- ../admin/all-the-sensitive-things/
那么這個(gè) API 請(qǐng)求將會(huì)變?yōu)?https://api.local/admin/all-the-sensitive-things/,是不是很兇險(xiǎn)?
解決方案依然是采用合適的 URL 構(gòu)建庫(kù)來(lái)為 URL 傳參,從而能正確地對(duì)參數(shù)進(jìn)行編碼。
42、采用合適的類(lèi)構(gòu)建路徑,避免路徑遍歷漏洞
就像 URL 地址一樣,如果攻擊者設(shè)法在路徑中的某個(gè)地方偷偷地插入 ../../../ ,文件路徑可能最終指向意料之外的位置。要避免這種情況,請(qǐng)創(chuàng)建一個(gè)類(lèi),采用這個(gè)類(lèi)安全地構(gòu)造路徑,并驗(yàn)證最終路徑是否在預(yù)期目錄中。避免在文件路徑中使用不受信任的數(shù)據(jù),或者更好的是,完全避免使用文件系統(tǒng),直接采用云存儲(chǔ)。
43、謹(jǐn)慎采用文件系統(tǒng),接收不受信任的內(nèi)容
如果允許用戶寫(xiě)入服務(wù)器的文件系統(tǒng),可能會(huì)出現(xiàn)各種各樣的問(wèn)題。改用云存儲(chǔ),或者在數(shù)據(jù)庫(kù)中使用二進(jìn)制 blob。
如果您必須訪問(wèn)磁盤(pán),則應(yīng)遵循以下指導(dǎo)原則:
- 不要讓不受信任的數(shù)據(jù)影響內(nèi)部文件路徑。
- 將文件保存在遠(yuǎn)離 webroot 的隔離目錄中。
- 在寫(xiě)入磁盤(pán)之前,請(qǐng)驗(yàn)證文件內(nèi)容是否與預(yù)期格式匹配。
- 正確設(shè)置文件系統(tǒng)權(quán)限以防止寫(xiě)入不需要的位置。
- 不要提取壓縮包(例如 ZIP),因?yàn)樗鼈兛梢园魏挝募?,包括指向系統(tǒng)任意地方的鏈接和路徑。
44、不要?jiǎng)討B(tài)執(zhí)行代碼,避免遠(yuǎn)程代碼執(zhí)行漏洞
不要使用 eval 或等效函數(shù)。找到一種其它的方法來(lái)實(shí)現(xiàn)代碼執(zhí)行。否則,不受信任的數(shù)據(jù)將有可能進(jìn)行函數(shù)調(diào)用,從而在有機(jī)會(huì)在服務(wù)器上執(zhí)行惡意代碼。
45、合理采用序列化,避免反序列化漏洞
對(duì)不受信任的數(shù)據(jù)進(jìn)行反序列化是很危險(xiǎn)的,很容易導(dǎo)致遠(yuǎn)程代碼執(zhí)行。
- 如果可以避免,不要使用序列化。
- 如果可以在服務(wù)器端序列化對(duì)象,則對(duì)其進(jìn)行數(shù)字簽名。當(dāng)需要再次反序列化它們時(shí),請(qǐng)?jiān)诶^續(xù)反序列化之前驗(yàn)證簽名。
- 使用一些主流的軟件組件,并保持更新。許多反序列化庫(kù)會(huì)一直被發(fā)現(xiàn)漏洞。GSon 是個(gè)不錯(cuò)的選擇。
- 使用簡(jiǎn)單的文本格式,如 JSON,而不是二進(jìn)制格式。此外,應(yīng)該避免像XML這樣有問(wèn)題的格式,因?yàn)檫@樣除了反序列化之外,還需要擔(dān)心 XML 漏洞。
- 在處理序列化對(duì)象之前驗(yàn)證它。例如:對(duì)于 JSON,在繼續(xù)反序列化之前,根據(jù)嚴(yán)格的 JSON 模式驗(yàn)證 JSON 文檔。
2.2 基礎(chǔ)設(shè)施
46、采用網(wǎng)絡(luò)應(yīng)用程序防火墻(WAF)
安裝防火墻,會(huì)減少很多風(fēng)險(xiǎn)。ModSecurity 就是一個(gè)很好的開(kāi)源選擇。
47、配置 Web 服務(wù)器,避免 HTTP desync 攻擊
HTTP desync,也稱(chēng) HTTP 請(qǐng)求走私攻擊,是指攻擊者劫持隨機(jī)用戶向系統(tǒng)發(fā)出的 HTTP 請(qǐng)求。這類(lèi)攻擊一般在以下情況下發(fā)生:
- 前端服務(wù)器,比如負(fù)載均衡器或反向代理服務(wù)器,接受攜帶有 Content-length、Transfer-Encoding 等頭部參數(shù)的請(qǐng)求時(shí),將請(qǐng)求未經(jīng)處理隨即傳遞到后臺(tái);
- 后臺(tái)接受該請(qǐng)求的服務(wù)器(通常是應(yīng)用服務(wù)器),采用(或被欺騙采用)一個(gè)不同于前端服務(wù)器的機(jī)制來(lái)確定 HTTP 請(qǐng)求從何處開(kāi)始、何處結(jié)束,比如前端服務(wù)器使用 Content-Length,而應(yīng)用服務(wù)器采用 Transfer-Encoding;
- 前端服務(wù)器重復(fù)利用與后端服務(wù)器的連接;
- 前端服務(wù)器在與后臺(tái)服務(wù)器連接時(shí)采用 HTTP/1(而非 HTTP/2)。
那么該如何進(jìn)行防范呢?一般是根據(jù)所采用的產(chǎn)品:
- 咨詢所采用的反向代理產(chǎn)品供應(yīng)商,確保該產(chǎn)品具備主動(dòng)防范攻擊的能力;
- 配置前端服務(wù)器,在與后臺(tái)連接時(shí)采用 HTTP/2;
- 配置前端服務(wù)器,防止利用同一個(gè)連接發(fā)送多個(gè)客戶端的 HTTP 請(qǐng)求;
- 采用網(wǎng)絡(luò)應(yīng)用程序防火墻(WAF),并確保其具備防止請(qǐng)求走私的模塊。
48、采用容器
讓目標(biāo)應(yīng)用隔離其他應(yīng)用來(lái)運(yùn)行。這樣,即使發(fā)生了攻擊事件,攻擊者也不會(huì)有權(quán)限去訪問(wèn)未經(jīng)許可的文件、系統(tǒng)或網(wǎng)絡(luò)資源。因此,最好使用 Kubernetes 或一個(gè)云端環(huán)境來(lái)部署你的應(yīng)用。如果因?yàn)槟撤N原因必須使用一臺(tái)服務(wù)器,那么可以手動(dòng)采用 Docker 來(lái)約束應(yīng)用。
49、使用 SELinux/AppArmor
即使通過(guò)容器來(lái)運(yùn)行應(yīng)用,也還是需要進(jìn)一步采用 SELinux 或 AppArmor 策略來(lái)進(jìn)一步地對(duì)應(yīng)用做出約束,從而減少容器漏洞引發(fā)的威脅。
50、采用最少權(quán)限的服務(wù)賬戶
這種方法帶來(lái)的好處是即使發(fā)生了被攻擊事件,也能減少被攻擊造成的損失。再次重申,列出所有的情形是不可能的,這里僅列舉一些例子幫助大家理解:
- 即使使用了 Docker,甚至是使用了 SELinux/AppArmor,不要用 root 賬戶來(lái)運(yùn)行你的應(yīng)用。為你的應(yīng)用單獨(dú)創(chuàng)建一個(gè)具備盡可能少的權(quán)限的賬戶,從而降低攻擊者利用容器或內(nèi)核漏洞等進(jìn)行攻擊的可能性;
- 如果有使用數(shù)據(jù)庫(kù),確保應(yīng)用程序中的數(shù)據(jù)庫(kù)用戶在訪問(wèn)數(shù)據(jù)庫(kù)時(shí)具備盡可能少的權(quán)限;
- 如果應(yīng)用中集成了 API,確保應(yīng)用訪問(wèn) API 時(shí)具備盡可能少的權(quán)限。
51、限制外部網(wǎng)絡(luò)連接
攻擊者通常需要建立一定的反向通信渠道來(lái)建立操控渠道或竊取數(shù)據(jù)。此外,一些漏洞也是需要外部網(wǎng)絡(luò)連接才會(huì)被發(fā)現(xiàn)、被利用。
因此,不能讓?xiě)?yīng)用隨便訪問(wèn)外部網(wǎng)絡(luò),包括 DNS。試下在服務(wù)器運(yùn)行命令 nslookup www.example.com,如果運(yùn)行成功,則說(shuō)明你沒(méi)有對(duì)外部網(wǎng)絡(luò)連接做出適當(dāng)?shù)南拗?。如何處理此?lèi)問(wèn)題,一般則取決于基礎(chǔ)設(shè)施。
針對(duì)外部的 TCP/UDP/ICMP 連接,一般可以通過(guò)以下方式禁用:
- 網(wǎng)關(guān)防火墻,如果有的話;
- 如果是老式服務(wù)器,可以采用本地的防火墻(例如 iptables 或 Windows 防火墻);
- 如果服務(wù)器端采用 Docker,可以使用 iptables;
- 如果使用了 Kubernetes,可采用網(wǎng)絡(luò)策略定義。
DNS 處理起來(lái)稍微麻煩一點(diǎn),我們通常需要允許對(duì)一些 hosts 的訪問(wèn)。
- 如果有本地的 hosts 文件,那就很簡(jiǎn)單,可以采取上面的任何一種方式來(lái)將 DNS 徹底禁用;
- 如果沒(méi)有,那么你需要在你上游的 DNS 中配置一個(gè)私有的區(qū)域,在網(wǎng)絡(luò)層限制僅能訪問(wèn)該指定的 DNS 服務(wù)器。這個(gè)私有區(qū)域內(nèi)只允許對(duì)一些預(yù)先指定的 hosts 的訪問(wèn)。
52、跟蹤 DNS 記錄,防止子域名劫持
子域名劫持發(fā)生場(chǎng)景舉例如下:
- 假如我們擁有一個(gè)域名 example.com;
- 針對(duì)一次促銷(xiāo)活動(dòng),我們買(mǎi)了另一個(gè)域名 www.my-cool-campaign.com ,然后創(chuàng)建了一個(gè)別名從 campaign.example.com 映射到 www.my-cool-campaign.com;
- 這次促銷(xiāo)活動(dòng)結(jié)束后,www.my-cool-campaign.com 域名也到期了;
- 但是,從 campaign.example.com 到 www.my-cool-campaign.com 的別名映射仍存在;
- 如果有人購(gòu)買(mǎi)了這個(gè)到期的域名,那么 campaign.example.com 便可以直接指向該域名;
- 如果攻擊者在 www.my-cool-campaign.com 域名下提供一些惡意內(nèi)容,那么便可以通過(guò) https://campaign.example.com 域名直接訪問(wèn)到。
因此,需要隨時(shí)留意你的 DNS 記錄。如果需要處理的類(lèi)似情況較多,強(qiáng)烈建議你做一個(gè)自動(dòng)監(jiān)控方案。
2.3 架構(gòu)
53、創(chuàng)建內(nèi)部 API 用來(lái)訪問(wèn)數(shù)據(jù)源
對(duì)連接互聯(lián)網(wǎng)的網(wǎng)絡(luò)應(yīng)用程序不應(yīng)該太過(guò)于信任。例如,不應(yīng)允許它進(jìn)行數(shù)據(jù)庫(kù)直連。否則,當(dāng)有人攻破應(yīng)用程序時(shí),整個(gè)數(shù)據(jù)庫(kù)都將面臨威脅。
相反,我們應(yīng)該搭建多組件組成的架構(gòu),例如:
- 我們域名為 www.example.com 的應(yīng)用程序使用 auth0 進(jìn)行鑒權(quán)。
- 該應(yīng)用程序訪問(wèn)內(nèi)部 API 服務(wù) api.example.local 時(shí),攜帶被授權(quán)用戶的 token,放在請(qǐng)求頭部的 Authorization 中。
- 位于 api.example.local 的 API 服務(wù)根據(jù)用戶的 token 進(jìn)行訪問(wèn)限制,進(jìn)而根據(jù)被授予的權(quán)限讀寫(xiě)數(shù)據(jù)庫(kù)。
假如現(xiàn)在有黑客想要攻破我們的應(yīng)用程序,即使成功,他也沒(méi)有權(quán)限訪問(wèn)整個(gè)數(shù)據(jù)庫(kù),而只是利用某個(gè)用戶的 token,進(jìn)而訪問(wèn)該 token 所允許訪問(wèn)的那部分?jǐn)?shù)據(jù)。
54、內(nèi)部連接也需加密和驗(yàn)證
不要盲目相信內(nèi)網(wǎng)的安全性,有很多方法可以攻破它。對(duì)于系統(tǒng)間的訪問(wèn),全部采用 TLS(也就是 HTTPS)進(jìn)行加密,最好在網(wǎng)絡(luò)和系統(tǒng)兩個(gè)層次對(duì)連接進(jìn)行鑒權(quán)。
55、對(duì)敏感信息集中管理
如果沒(méi)有采用合適的敏感信息管理方案,就很難保持授權(quán)的短期性化、可審計(jì)性和秘密性。因此,建議采用 HashiCorp Vault 一類(lèi)的工具來(lái)集中管理密碼、加密 key 等類(lèi)似信息。
2.4 監(jiān)控
56、收集,分析,報(bào)警
集中收集日志到一個(gè)獨(dú)立系統(tǒng),比如 SIEM(安全信息和事件監(jiān)控系統(tǒng))。在這個(gè)系統(tǒng)中,可以在一些表征脆弱性、攻擊的事件發(fā)生時(shí)進(jìn)行報(bào)警。當(dāng)嚴(yán)重威脅發(fā)生時(shí),可以立即通知相關(guān)人員。
57、收集系統(tǒng)安全事件
最重要的日志來(lái)源可能就是系統(tǒng)自身了。當(dāng)有可疑行為發(fā)生時(shí),系統(tǒng)應(yīng)能引發(fā)異常,記錄事件,可能的話,甚至可以自動(dòng)封鎖可能帶來(lái)問(wèn)題的用戶或IP地址。常見(jiàn)可疑行為包括:
- 輸入值的合法性驗(yàn)證錯(cuò)誤(例如,試圖輸入 UI 中不可能提供的值)
- 訪問(wèn)控制錯(cuò)誤 (例如,嘗試訪問(wèn)一條在 UI 中不可能出現(xiàn)的記錄)
- 數(shù)據(jù)庫(kù)語(yǔ)法錯(cuò)誤表示某個(gè)人發(fā)現(xiàn)了一處 SQL 注入的脆弱性,這時(shí)候可要?jiǎng)幼骺禳c(diǎn)采取行動(dòng)了
- XML 錯(cuò)誤表示某個(gè)人發(fā)現(xiàn)了一處 XML 注入的脆弱性,或者正嘗試?yán)?XXE(XML 外部實(shí)體)脆弱性進(jìn)行攻擊
- 錯(cuò)誤請(qǐng)求表示用戶可能發(fā)送了被應(yīng)用拒絕的請(qǐng)求。Spring 框架的 RequstRejectedException 就是一個(gè)例子
- 反跨站請(qǐng)求偽造令牌驗(yàn)證錯(cuò)誤一般表示有人正嘗試尋找系統(tǒng)中存在的脆弱性
58、收集運(yùn)行時(shí)安全日志
使用運(yùn)行時(shí)安全監(jiān)控工具如 Falco 來(lái)對(duì)異常系統(tǒng)訪問(wèn)進(jìn)行檢測(cè)。如果采用了 Kubernetes,那么 Falco 就特別有用。遠(yuǎn)程也可以對(duì)日志進(jìn)行收集和監(jiān)控。
59、收集 SELinux/AppArmor 日志
假如我們制定了 SELinux 策略防止向外部的連接,但系統(tǒng)忽然向外部某個(gè)網(wǎng)站(例如 burpcollaborator.net)發(fā)起 HTTP 請(qǐng)求,那就需要立刻引起關(guān)注。又或者你的系統(tǒng)嘗試訪問(wèn) /etc/passwd。這兩種情況都表示有人已經(jīng)發(fā)現(xiàn)了我們系統(tǒng)中的漏洞。
60、收集 Web 服務(wù)器事件
對(duì) Web 服務(wù)器軟件,至少要對(duì)訪問(wèn)日志和錯(cuò)誤日志進(jìn)行收集,收集后發(fā)送到集中式的日志服務(wù)器。在突發(fā)事件響應(yīng)時(shí),這將輔助我們快速理清時(shí)間線。
61、收集網(wǎng)絡(luò)應(yīng)用程序防火墻(WAF)日志
如果你像上文推薦使用了網(wǎng)絡(luò)應(yīng)用程序防火墻(WAF),那么也對(duì)這個(gè)日志進(jìn)行收集。但不用針對(duì)這個(gè)日志設(shè)置報(bào)警,因?yàn)樗旧蠒?huì)收到來(lái)自互聯(lián)網(wǎng)各種各樣的問(wèn)題,而且不部分是你不用擔(dān)心的。
2.5 事件響應(yīng)
62、制定應(yīng)對(duì)計(jì)劃
一旦對(duì)我們的系統(tǒng)進(jìn)行了監(jiān)控和加固,攻擊者將難以快速定位系統(tǒng)漏洞,即使最終發(fā)現(xiàn),我們也能快速了解情況。
但僅了解情況是不夠的,還需要做出如下準(zhǔn)備:
- 快速分析系統(tǒng)日志,了解當(dāng)前狀況和需采取的對(duì)應(yīng)措施
- 在應(yīng)用防火墻等產(chǎn)品中,快速對(duì)個(gè)別 url 地址和參數(shù)做出限制
- 如有需要,快速關(guān)停系統(tǒng)
2.6 開(kāi)發(fā)管理
63、威脅模型
系統(tǒng)地考慮一下“可能會(huì)出現(xiàn)哪些問(wèn)題”并據(jù)此做出調(diào)整。設(shè)計(jì)一個(gè)新的系統(tǒng)時(shí),越早開(kāi)始這一步越好。當(dāng)對(duì)系統(tǒng)發(fā)生改變時(shí),再重新梳理一遍這個(gè)過(guò)程。
例如:
小王:如果攻擊者攻破了我們連接了互聯(lián)網(wǎng)的服務(wù)器,怎么辦?
小陳:那可就完蛋了!
小王:好吧!這就說(shuō)明我們?cè)谶@里存在著一個(gè)信任關(guān)系,我們認(rèn)為連接了互聯(lián)網(wǎng)的服務(wù)器是不會(huì)被攻破的。我們可以信任這一點(diǎn)嗎?
小陳:未必吧!有一百種可能導(dǎo)致我們的服務(wù)器被黑掉,例如我們代碼中存在的脆弱性,或者依賴(lài)中存在的脆弱性,或者是我們 Web 服務(wù)器所安裝軟件的脆弱性。
小王:好吧!那就讓我們打破這層信任關(guān)系。接下來(lái)該做些什么呢?
小陳:我們這樣來(lái)分解一下系統(tǒng):創(chuàng)建一些內(nèi)部的接口用來(lái)實(shí)際訪問(wèn)數(shù)據(jù)庫(kù),由此以來(lái),前端的 Web 服務(wù)器就不能直接訪問(wèn)后臺(tái)的所有東西。
小王:這是個(gè)好辦法!除此以外,還有其它什么可能出問(wèn)題呢?
小陳:嗯,如果黑客攻破了我們的內(nèi)網(wǎng)呢?
小王:那所有東西都要丟失了,因?yàn)閮?nèi)網(wǎng)里服務(wù)器之間的連接都是未加密的。
小陳:……
這就是威脅模型,它不需要多么復(fù)雜。使用這種方式,來(lái)找出系統(tǒng)中可能存在的威脅。
64、源代碼強(qiáng)制審查
通過(guò)技術(shù)控制手段,防止代碼未經(jīng)他人審核便提交入庫(kù)。這是構(gòu)建安全開(kāi)發(fā)環(huán)境的基礎(chǔ),因?yàn)樗梢宰龅剑?/p>
- 如果攻擊者攻陷了一個(gè)開(kāi)發(fā)人員的電腦,或者是開(kāi)發(fā)人員自身企圖發(fā)起攻擊,將不能直接將惡意代碼遷入代碼庫(kù);
- 如果開(kāi)發(fā)人員的錯(cuò)誤導(dǎo)致引入了有漏洞的代碼,很可能在被其他人檢查時(shí)及時(shí)發(fā)現(xiàn)。
65、自動(dòng)化持續(xù)集成管道,僅允許簡(jiǎn)單訪問(wèn)
開(kāi)發(fā)人員應(yīng)該有權(quán)限觸發(fā) Jenkins 構(gòu)建,且 Jenkins 權(quán)限配置也僅該如此,不要再允許其它權(quán)限。單個(gè)開(kāi)發(fā)人員應(yīng)該不能在構(gòu)建階段引入任意代碼。當(dāng)然,如果像上文推薦的強(qiáng)制性地采用了代碼審查,Jenkinsfile 也可以保存在版本管理工具中。
66、對(duì) artifacts 進(jìn)行簽名
如果是構(gòu)建容器鏡像,可以把對(duì)鏡像簽名作為構(gòu)建的一步。將簽名密鑰存儲(chǔ)在安全的地方。構(gòu)建階段需要訪問(wèn)密鑰,但是杜絕將密鑰與 Jenkinsfile 一起存儲(chǔ)在版本管理工具中。更好的方式是將密鑰存儲(chǔ)在 HashiCorp Vault 之類(lèi)的地方,然后在構(gòu)建時(shí)再進(jìn)行拉取。
67、持續(xù)集成管道中加入靜態(tài)應(yīng)用程序掃描器
在持續(xù)集成管道中使用 SpotBugs 和 Find-Sec-Bugs(或者根據(jù)你所采用的技術(shù)棧進(jìn)行選擇)之類(lèi)的工具。它們可以幫你在部署代碼之前發(fā)現(xiàn)已知的漏洞。
此外,也可以作為 IDE 的插件安裝在開(kāi)發(fā)人員的電腦上,在代碼遷入之前就運(yùn)行這些工具進(jìn)行檢查。
68、構(gòu)建時(shí)對(duì)依賴(lài)進(jìn)行檢查,保證最小的依賴(lài)集
應(yīng)用程序中依賴(lài)的每個(gè)軟件包都是一個(gè)風(fēng)險(xiǎn)來(lái)源。通過(guò)依賴(lài),我們拉取了第三人的代碼并在我們的應(yīng)用服務(wù)器上執(zhí)行,所以,必須要搞清楚我們依賴(lài)的這個(gè)軟件包是什么,為什么會(huì)依賴(lài)它?
- 保持最小的依賴(lài)集;
- 僅使用我們所信任的依賴(lài)。它們必須是廣泛使用和廣為人知的;
- 采用構(gòu)建框架,對(duì)依賴(lài)進(jìn)行確認(rèn)。
此外,嚴(yán)格控制應(yīng)用服務(wù)器的對(duì)外連接,從而避免后門(mén)的存在。
69、對(duì)依賴(lài)進(jìn)行安全掃描
使用 OWASP 依賴(lài)檢查工具對(duì)依賴(lài)中常見(jiàn)的安全問(wèn)題進(jìn)行掃描。除了在持續(xù)集成管道中,也可以在開(kāi)發(fā)人員的開(kāi)發(fā)環(huán)境運(yùn)行這些工具。
70、持續(xù)集成管道對(duì)鏡像進(jìn)行安全掃描
如果采用了容器化技術(shù),可以使用 Trivy 等工具對(duì)容器鏡像進(jìn)行一些常規(guī)漏洞的掃描。
71、自動(dòng)化部署和簽名驗(yàn)證
開(kāi)發(fā)人員可以有權(quán)限到生產(chǎn)環(huán)境中部署,但是權(quán)限范圍應(yīng)該控制在前階段已經(jīng)構(gòu)建和簽名過(guò)的特定鏡像,而不是直接訪問(wèn)生產(chǎn)服務(wù)器。如果是使用 Kubernetes,可以通過(guò) Notary 或開(kāi)放策略代理來(lái)驗(yàn)證待部署鏡像的簽名。
72、設(shè)置一個(gè)安全人員
一個(gè)人的精力是有限的。我們不能期望每個(gè)開(kāi)發(fā)人員都精通滲透測(cè)試或是安全工程師。正如你不能期望所有的安全專(zhuān)家都是優(yōu)秀的開(kāi)發(fā)人員一樣。因此,可以在團(tuán)隊(duì)中設(shè)置一個(gè)專(zhuān)門(mén)關(guān)注安全的人員,主要與開(kāi)發(fā)人員、架構(gòu)師進(jìn)行交流,幫助保護(hù)我們的應(yīng)用程序并在團(tuán)隊(duì)中傳播安全意識(shí)。
三、結(jié)論
保證應(yīng)用程序的安全性,光靠避免漏洞時(shí)不夠的,必須全面通盤(pán)考慮,主動(dòng)進(jìn)行防御。這里對(duì)一些主要方法進(jìn)行了總結(jié):
使用最新版本的的軟件組件來(lái)執(zhí)行危險(xiǎn)的操作,如身份驗(yàn)證、訪問(wèn)控制、加密、訪問(wèn)數(shù)據(jù)庫(kù)或解析 XML,并確保正確配置了這些組件,例如 XML 解析時(shí)禁用外部實(shí)體。
- 使用平臺(tái)提供的安全控制,例如反跨站請(qǐng)求偽造保護(hù)。
- 使用 Web 瀏覽器提供的安全控件,如 HSTS、SameSite Cookie 和內(nèi)容安全策略。
- 對(duì)安全控制進(jìn)行集中化處理,特別是身份驗(yàn)證和訪問(wèn)控制,從而避免一些遺漏,如在某些控制器方法上忘記對(duì)安全進(jìn)行控制。
- 使用 Web 應(yīng)用程序防火墻,防止應(yīng)用程序漏洞被發(fā)現(xiàn)和被利用。
- 通過(guò)限制對(duì)文件、網(wǎng)絡(luò)和系統(tǒng)資源的訪問(wèn)來(lái)對(duì)應(yīng)用程序進(jìn)行限制。
- 利用威脅模型發(fā)現(xiàn)架構(gòu)中的威脅,并相應(yīng)進(jìn)行處理。既包括在源代碼層面對(duì)每個(gè)開(kāi)發(fā)人員的源代碼進(jìn)行安全控制,也包括在架構(gòu)層面對(duì)前端 Web 服務(wù)器的安全控制。
- 對(duì)系統(tǒng)進(jìn)行監(jiān)控,制定異常處理預(yù)案。
- 在開(kāi)發(fā)環(huán)境和持續(xù)集成環(huán)境中使用漏洞掃描程序?qū)Υa、鏡像、依賴(lài)進(jìn)行掃描。
- 對(duì)開(kāi)發(fā)人員、架構(gòu)師等開(kāi)展安全培訓(xùn),并在團(tuán)隊(duì)中配備一名安全人員。
感謝原作者的翻譯授權(quán):