Web 應(yīng)用中存在很多安全風(fēng)險(xiǎn),這些風(fēng)險(xiǎn)會(huì)被黑客利用,輕則篡改網(wǎng)頁(yè)內(nèi)容,重則竊取網(wǎng)站內(nèi)部數(shù)據(jù),更為嚴(yán)重的則是在網(wǎng)頁(yè)中植入惡意代碼,使得用戶受到侵害。常見(jiàn)的安全漏洞如下:
而框架本身針對(duì) Web 端常見(jiàn)的安全風(fēng)險(xiǎn)內(nèi)置了豐富的解決方案:
在框架中內(nèi)置了安全插件 egg-security, 提供了默認(rèn)的安全實(shí)踐。
注意:除非清楚的確認(rèn)后果,否則不建議擅自關(guān)閉安全插件提供的功能。
框架的安全插件是默認(rèn)開(kāi)啟的,如果我們想關(guān)閉其中一些安全防范,直接設(shè)置該項(xiàng)的 enable 屬性為 false 即可。例如關(guān)閉 xframe 防范:
exports.security = { |
match 和 ignore 使用方法和格式與中間件通用配置一致。
如果只想開(kāi)啟針對(duì)某一路徑,則配置 match 選項(xiàng),例如只針對(duì) /example 開(kāi)啟 CSP:
exports.security = { |
如果需要針對(duì)某一路徑忽略某安全選項(xiàng),則配置 ignore 選項(xiàng),例如針對(duì) /example 關(guān)閉 xframe,以便合作商戶能夠嵌入我們的頁(yè)面:
exports.security = { |
如果要針對(duì)內(nèi)部 ip 關(guān)閉部分安全防范:
exports.security = { |
下面我們會(huì)針對(duì)具體的場(chǎng)景,來(lái)講解如何使用框架提供的安全方案進(jìn)行 Web 安全防范。
XSS(cross-site scripting跨域腳本攻擊)攻擊是最常見(jiàn)的 Web 攻擊,其重點(diǎn)是『跨域』和『客戶端執(zhí)行』。
XSS 攻擊一般分為兩類(lèi):
反射型的 XSS 攻擊,主要是由于服務(wù)端接收到客戶端的不安全輸入,在客戶端觸發(fā)執(zhí)行從而發(fā)起 Web 攻擊。比如:
在某購(gòu)物網(wǎng)站搜索物品,搜索結(jié)果會(huì)顯示搜索的關(guān)鍵詞。搜索關(guān)鍵詞填入<script>alert('handsome boy')</script>, 點(diǎn)擊搜索。頁(yè)面沒(méi)有對(duì)關(guān)鍵詞進(jìn)行過(guò)濾,這段代碼就會(huì)直接在頁(yè)面上執(zhí)行,彈出 alert。
框架提供了 helper.escape() 方法對(duì)字符串進(jìn)行 XSS 過(guò)濾。
const str = '><script>alert("abc") </script><'; |
當(dāng)網(wǎng)站需要直接輸出用戶輸入的結(jié)果時(shí),請(qǐng)務(wù)必使用 helper.escape() 包裹起來(lái),如在 egg-view-nunjucks 里面就覆蓋掉了內(nèi)置的 escape。
另外一種情況,網(wǎng)站輸出的內(nèi)容會(huì)提供給 JavaScript 來(lái)使用。這個(gè)時(shí)候需要使用 helper.sjs() 來(lái)進(jìn)行過(guò)濾。
helper.sjs() 用于在 JavaScript(包括 onload 等 event)中輸出變量,會(huì)對(duì)變量中字符進(jìn)行 JavaScript ENCODE, 將所有非白名單字符轉(zhuǎn)義為 \x 形式,防止 XSS 攻擊,也確保在 js 中輸出的正確性。使用實(shí)例:
const foo = '"hello"'; |
還有一種情況,有時(shí)候我們需要在 JavaScript 中輸出 json ,若未做轉(zhuǎn)義,易被利用為 XSS 漏洞??蚣芴峁┝?nbsp;helper.sjson() 宏做 json encode,會(huì)遍歷 json 中的 key ,將 value 的值中,所有非白名單字符轉(zhuǎn)義為 \x 形式,防止 XSS 攻擊。同時(shí)保持 json 結(jié)構(gòu)不變。 若存在模板中輸出一個(gè) JSON 字符串給 JavaScript 使用的場(chǎng)景,請(qǐng)使用 helper.sjson(變量名) 進(jìn)行轉(zhuǎn)義。
處理過(guò)程較復(fù)雜,性能損耗較大,請(qǐng)僅在必要時(shí)使用。
實(shí)例:
<script> |
基于存儲(chǔ)的 XSS 攻擊,是通過(guò)提交帶有惡意腳本的內(nèi)容存儲(chǔ)在服務(wù)器上,當(dāng)其他人看到這些內(nèi)容時(shí)發(fā)起 Web 攻擊。一般提交的內(nèi)容都是通過(guò)一些富文本編輯器編輯的,很容易插入危險(xiǎn)代碼。
框架提供了 helper.shtml() 方法對(duì)字符串進(jìn)行 XSS 過(guò)濾。
注意,將富文本(包含 HTML 代碼的文本)當(dāng)成變量直接在模版里面輸出時(shí),需要用到 shtml 來(lái)處理。 使用 shtml 可以輸出 HTML 的 tag,同時(shí)執(zhí)行 XSS 的過(guò)濾動(dòng)作,過(guò)濾掉非法的腳本。
由于是一個(gè)非常復(fù)雜的安全處理過(guò)程,對(duì)服務(wù)器處理性能一定影響,如果不是輸出 HTML,請(qǐng)勿使用。
簡(jiǎn)單示例:
// js |
|
shtml 在 xss 模塊基礎(chǔ)上增加了針對(duì)域名的過(guò)濾。
例如只支持 a 標(biāo)簽,且除了 title 其他屬性都過(guò)濾掉: whiteList: {a: ['title']}
options:
注意,shtml 使用了嚴(yán)格的白名單機(jī)制,除了過(guò)濾掉 XSS 風(fēng)險(xiǎn)的字符串外, 在默認(rèn)規(guī)則外的 tag 和 attr 都會(huì)被過(guò)濾掉。
例如 HTML 標(biāo)簽就不在白名單中,
const html = '<html></html>'; |
常見(jiàn)的 data-xx 屬性由于不在白名單中,所以都會(huì)被過(guò)濾。
所以,一定要注意 shtml 的適用場(chǎng)景,一般是針對(duì)來(lái)自用戶的富文本輸入,切忌濫用,功能既受到限制,又會(huì)影響服務(wù)端性能。 此類(lèi)場(chǎng)景一般是論壇、評(píng)論系統(tǒng)等,即便是論壇等如果不支持 HTML 內(nèi)容輸入,也不要使用此 Helper,直接使用 escape 即可。
JSONP 的 callback 參數(shù)非常危險(xiǎn),他有兩種風(fēng)險(xiǎn)可能導(dǎo)致 XSS
1、callback 參數(shù)意外截?cái)鄇s代碼,特殊字符單引號(hào)雙引號(hào),換行符均存在風(fēng)險(xiǎn)。
2、callback 參數(shù)惡意添加標(biāo)簽(如 <script> ),造成 XSS 漏洞。
參考 JSONP 安全攻防
框架內(nèi)部使用 jsonp-body 來(lái)對(duì) JSONP 請(qǐng)求進(jìn)行安全防范。
防御內(nèi)容:
可定義配置:
瀏覽器自身具有一定針對(duì)各種攻擊的防范能力,他們一般是通過(guò)開(kāi)啟 Web 安全頭生效的??蚣軆?nèi)置了一些常見(jiàn)的 Web 安全頭的支持。
W3C 的 Content Security Policy,簡(jiǎn)稱 CSP,主要是用來(lái)定義頁(yè)面可以加載哪些資源,減少 XSS 的發(fā)生。
框架內(nèi)支持 CSP 的配置,不過(guò)是默認(rèn)關(guān)閉的,開(kāi)啟后可以有效的防止 XSS 攻擊的發(fā)生。要配置 CSP , 需要對(duì) CSP 的 policy 策略有了解,具體細(xì)節(jié)可以參考 CSP 是什么。
默認(rèn)開(kāi)啟,禁用 IE 下下載框Open按鈕,防止 IE 下下載文件默認(rèn)被打開(kāi) XSS。
禁用 IE8 自動(dòng)嗅探 mime 功能例如 text/plain 卻當(dāng)成 text/html 渲染,特別當(dāng)本站點(diǎn) serve 的內(nèi)容未必可信的時(shí)候。
IE 提供的一些 XSS 檢測(cè)與防范,默認(rèn)開(kāi)啟
CSRF(Cross-site request forgery跨站請(qǐng)求偽造,也被稱為 One Click Attack 或者 Session Riding,通??s寫(xiě)為 CSRF 或者 XSRF,是一種對(duì)網(wǎng)站的惡意利用。 CSRF 攻擊會(huì)對(duì)網(wǎng)站發(fā)起惡意偽造的請(qǐng)求,嚴(yán)重影響網(wǎng)站的安全。因此框架內(nèi)置了 CSRF 防范方案。
通常來(lái)說(shuō),對(duì)于 CSRF 攻擊有一些通用的防范方案,簡(jiǎn)單的介紹幾種常用的防范方案:
框架結(jié)合了上述幾種防范方式,提供了一個(gè)可配置的 CSRF 防范策略。
在同步渲染頁(yè)面時(shí),在表單請(qǐng)求中增加一個(gè) name 為 _csrf 的 url query,值為 ctx.csrf,這樣用戶在提交這個(gè)表單的時(shí)候會(huì)將 CSRF token 提交上來(lái):
<form method="POST" action="/upload?_csrf={{ ctx.csrf | safe }}" enctype="multipart/form-data"> |
傳遞 CSRF token 的字段可以在配置中改變:
// config/config.default.js |
為了防范 BREACH 攻擊,通過(guò)同步方式渲染到頁(yè)面上的 CSRF token 在每次請(qǐng)求時(shí)都會(huì)變化,egg-view-nunjucks 等 View 插件會(huì)自動(dòng)對(duì) Form 進(jìn)行注入,對(duì)應(yīng)用開(kāi)發(fā)者無(wú)感知。
在 CSRF 默認(rèn)配置下,token 會(huì)被設(shè)置在 Cookie 中,在 AJAX 請(qǐng)求的時(shí)候,可以從 Cookie 中取到 token,放置到 query、body 或者 header 中發(fā)送給服務(wù)端。
In jQuery:
var csrftoken = Cookies.get('csrfToken'); |
通過(guò) header 傳遞 CSRF token 的字段也可以在配置中改變:
// config/config.default.js |
默認(rèn)配置下,框架會(huì)將 CSRF token 存在 Cookie 中,以方便 AJAX 請(qǐng)求獲取到。但是所有的子域名都可以設(shè)置 Cookie,因此當(dāng)我們的應(yīng)用處于無(wú)法保證所有的子域名都受控的情況下,存放在 Cookie 中可能有被 CSRF 攻擊的風(fēng)險(xiǎn)。框架提供了一個(gè)配置項(xiàng),可以將 token 存放到 Session 中。
// config/config.default.js |
注意:該選項(xiàng)已廢棄,攻擊者可以通過(guò) flash + 307 來(lái)攻破,請(qǐng)不要在生產(chǎn)環(huán)境打開(kāi)改選項(xiàng)!
在 SOP 的安全策略保護(hù)下,基本上所有的現(xiàn)代瀏覽器都不允許跨域發(fā)起 content-type 為 JSON 的請(qǐng)求,因此我們可以直接放過(guò)類(lèi)型的 JSON 格式的請(qǐng)求。
// config/config.default.js |
當(dāng) CSRF token 存儲(chǔ)在 Cookie 中時(shí),一旦在同一個(gè)瀏覽器上發(fā)生用戶切換,新登陸的用戶將會(huì)依舊使用舊的 token(之前用戶使用的),這會(huì)帶來(lái)一定的安全風(fēng)險(xiǎn),因此在每次用戶登陸的時(shí)候都必須刷新 CSRF token。
// login controller |
XST 的全稱是 Cross-Site Tracing,客戶端發(fā) TRACE 請(qǐng)求至服務(wù)器,如果服務(wù)器按照標(biāo)準(zhǔn)實(shí)現(xiàn)了 TRACE 響應(yīng),則在 response body 里會(huì)返回此次請(qǐng)求的完整頭信息。通過(guò)這種方式,客戶端可以獲取某些敏感的頭字段,例如 httpOnly 的 Cookie。
下面我們基于 Koa 來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的支持 TRACE 方法的服務(wù)器:
var koa = require('koa'); |
啟動(dòng)服務(wù)后,先發(fā)個(gè) GET 請(qǐng)求 curl -i http://127.0.0.1:7001,得到如下響應(yīng):
HTTP/1.1 200 OK |
服務(wù)器設(shè)置了一個(gè) httpOnly 的 Cookie 為 1,在瀏覽器環(huán)境中,是無(wú)法通過(guò)腳本獲取它的。
接著我們發(fā) TRACE 請(qǐng)求到服務(wù)器curl -X TRACE -b a=1 -i http://127.0.0.1:7001,并帶上 Cookie,得到如下響應(yīng):
HTTP/1.1 200 OK |
在響應(yīng)體里可以看到完整的頭信息,這樣我們就繞過(guò)了 httpOnly 的限制,拿到了cookie=1,造成了很大的風(fēng)險(xiǎn)。
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
http://deadliestwebattacks.com/2010/05/18/cross-site-tracing-xst-the-misunderstood-vulnerability/
框架已經(jīng)禁止了 trace,track,options 三種危險(xiǎn)類(lèi)型請(qǐng)求。
釣魚(yú)有多種方式,這里介紹 url 釣魚(yú)、圖片釣魚(yú)和 iframe 釣魚(yú)。
服務(wù)端未對(duì)傳入的跳轉(zhuǎn) url 變量進(jìn)行檢查和控制,可能導(dǎo)致可惡意構(gòu)造任意一個(gè)惡意地址,誘導(dǎo)用戶跳轉(zhuǎn)到惡意網(wǎng)站。 由于是從可信的站點(diǎn)跳轉(zhuǎn)出去的,用戶會(huì)比較信任,所以跳轉(zhuǎn)漏洞一般用于釣魚(yú)攻擊,通過(guò)轉(zhuǎn)到惡意網(wǎng)站欺騙用戶輸入用戶名和密碼盜取用戶信息,或欺騙用戶進(jìn)行金錢(qián)交易; 也可能引發(fā)的 XSS 漏洞(主要是跳轉(zhuǎn)常常使用 302 跳轉(zhuǎn),即設(shè)置 HTTP 響應(yīng)頭,Locatioin: url,如果 url 包含了 CRLF,則可能隔斷了 HTTP 響應(yīng)頭,使得后面部分落到了 HTTP body,從而導(dǎo)致 XSS 漏洞)。
框架提供了安全跳轉(zhuǎn)的方法,可以通過(guò)配置白名單避免這種風(fēng)險(xiǎn)。
安全方案覆蓋了默認(rèn)的ctx.redirect方法,所有的跳轉(zhuǎn)均會(huì)經(jīng)過(guò)安全域名的判斷。
用戶如果使用ctx.redirect方法,需要在應(yīng)用的配置文件中做如下配置:
// config/config.default.js |
若用戶沒(méi)有配置 domainWhiteList 或者 domainWhiteList數(shù)組內(nèi)為空,則默認(rèn)會(huì)對(duì)所有跳轉(zhuǎn)請(qǐng)求放行,即等同于ctx.unsafeRedirect(url)
如果可以允許用戶向網(wǎng)頁(yè)里插入未經(jīng)驗(yàn)證的外鏈圖片,這有可能出現(xiàn)釣魚(yú)風(fēng)險(xiǎn)。
比如常見(jiàn)的 401釣魚(yú), 攻擊者在訪問(wèn)頁(yè)面時(shí),頁(yè)面彈出驗(yàn)證頁(yè)面讓用戶輸入帳號(hào)及密碼,當(dāng)用戶輸入之后,帳號(hào)及密碼就存儲(chǔ)到了黑客的服務(wù)器中。 通常這種情況會(huì)出現(xiàn)在<img src=$url />中,系統(tǒng)不對(duì)$url是否在域名白名單內(nèi)進(jìn)行校驗(yàn)。
攻擊者可以在自己的服務(wù)器中構(gòu)造以下代碼:
401.php:作用為彈出 401 窗口,并且記錄用戶信息。
<?php |
之后攻擊者生成一個(gè)圖片鏈接<img src="http://xxx.xxx.xxx/fishing/401.php?a.jpg//" rel="external nofollow" />。
當(dāng)用戶訪問(wèn)時(shí),會(huì)彈出信息讓用戶點(diǎn)擊,用戶輸入的用戶名及密碼會(huì)被黑客的服務(wù)器偷偷記錄。
框架提供了 .surl() 宏做 url 過(guò)濾。
用于在 html 標(biāo)簽中中要解析 url 的地方(比如 <a href=""/><img src=""/>),其他地方不允許使用。
對(duì)模板中要輸出的變量,加 helper.surl($value)。
注意:在需要解析 url 的地方,surl 外面一定要加上雙引號(hào),否則就會(huì)導(dǎo)致XSS漏洞。
不使用 surl
<a href="$value" /> |
output:
<a rel="external nofollow" target="_blank" /> |
使用 surl
<a href="helper.surl($value)" /> |
output:
<a rel="external nofollow" target="_blank" /> |
iframe 釣魚(yú),通過(guò)內(nèi)嵌 iframe 到被攻擊的網(wǎng)頁(yè)中,攻擊者可以引導(dǎo)用戶去點(diǎn)擊 iframe 指向的危險(xiǎn)網(wǎng)站,甚至遮蓋,影響網(wǎng)站的正常功能,劫持用戶的點(diǎn)擊操作。
框架提供了 X-Frame-Options 這個(gè)安全頭來(lái)防止 iframe 釣魚(yú)。默認(rèn)值為 SAMEORIGIN,只允許同域把本頁(yè)面當(dāng)作 iframe 嵌入。
當(dāng)需要嵌入一些可信的第三方網(wǎng)頁(yè)時(shí),可以關(guān)閉這個(gè)配置。
Http Parameter Pollution(HPP),即 HTTP 參數(shù)污染攻擊。在HTTP協(xié)議中是允許同樣名稱的參數(shù)出現(xiàn)多次,而由于應(yīng)用的實(shí)現(xiàn)不規(guī)范,攻擊者通過(guò)傳播參數(shù)的時(shí)候傳輸 key 相同而 value 不同的參數(shù),從而達(dá)到繞過(guò)某些防護(hù)的后果。
HPP 可能導(dǎo)致的安全威脅有:
框架本身會(huì)在客戶端傳輸 key 相同而 value 不同的參數(shù)時(shí),強(qiáng)制使用第一個(gè)參數(shù),因此不會(huì)導(dǎo)致 hpp 攻擊。
HTTP 是網(wǎng)絡(luò)應(yīng)用廣泛使用的協(xié)議,負(fù)責(zé) Web 內(nèi)容的請(qǐng)求和獲取。然而,內(nèi)容請(qǐng)求和獲取時(shí)會(huì)經(jīng)過(guò)許多中間人,主要是網(wǎng)絡(luò)環(huán)節(jié),充當(dāng)內(nèi)容入口的瀏覽器、路由器廠商、WIFI提供商、通信運(yùn)營(yíng)商,如果使用了代理、翻墻軟件則會(huì)引入更多中間人。由于 HTTP 請(qǐng)求的路徑、參數(shù)默認(rèn)情況下均是明文的,因此這些中間人可以對(duì) HTTP 請(qǐng)求進(jìn)行監(jiān)控、劫持、阻擋。
在沒(méi)有 HTTPS 時(shí),運(yùn)營(yíng)商可在用戶發(fā)起請(qǐng)求時(shí)直接跳轉(zhuǎn)到某個(gè)廣告,或者直接改變搜索結(jié)果插入自家的廣告。如果劫持代碼出現(xiàn)了 BUG ,則直接讓用戶無(wú)法使用,出現(xiàn)白屏。
數(shù)據(jù)泄露、請(qǐng)求劫持、內(nèi)容篡改等等問(wèn)題,核心原因就在于 HTTP 是全裸式的明文請(qǐng)求,域名、路徑和參數(shù)都被中間人們看得一清二楚。HTTPS 做的就是給請(qǐng)求加密,讓其對(duì)用戶更加安全。對(duì)于自身而言除了保障用戶利益外,還可避免本屬于自己的流量被挾持,以保護(hù)自身利益。
盡管 HTTPS 并非絕對(duì)安全,掌握根證書(shū)的機(jī)構(gòu)、掌握加密算法的組織同樣可以進(jìn)行中間人形式的攻擊。不過(guò)HTTPS是現(xiàn)行架構(gòu)下最安全的解決方案,并且它大幅增加了中間人攻擊的成本。
因此,請(qǐng)各位使用 Egg 框架開(kāi)發(fā)網(wǎng)站的開(kāi)發(fā)者,務(wù)必推動(dòng)自己的網(wǎng)站升級(jí)到 HTTPS。
對(duì)于 HTTPS 來(lái)講,還有一點(diǎn)要注意的是 HTTP 嚴(yán)格傳輸安全(HSTS),如果不使用 HSTS,當(dāng)用戶在瀏覽器中輸入網(wǎng)址時(shí)沒(méi)有加 HTTPS,瀏覽器會(huì)默認(rèn)使用 HTTP 訪問(wèn)
框架默認(rèn)關(guān)閉了 hsts Strict-Transport-Security。使得 HTTPS 站點(diǎn)不跳轉(zhuǎn)到 HTTP,如果站點(diǎn)支持 HTTPS,請(qǐng)一定要開(kāi)啟。
如果我們的Web 站點(diǎn)是 http 站點(diǎn),需要關(guān)閉這個(gè)頭。配置如下:
通過(guò) Server-Side Request Forgery(SSRF) 攻擊,攻擊者可以發(fā)起網(wǎng)絡(luò)請(qǐng)求訪問(wèn)或者操作內(nèi)部網(wǎng)絡(luò)的資源。
一般來(lái)說(shuō),SSRF 安全漏洞常見(jiàn)于開(kāi)發(fā)者在服務(wù)端直接請(qǐng)求客戶端傳遞進(jìn)來(lái)的 URL 資源,一旦攻擊者傳入一些內(nèi)部的 URL 即可發(fā)起 SSRF 攻擊。
通常我們會(huì)基于內(nèi)網(wǎng) IP 黑名單的形式來(lái)防范 SSRF 攻擊,通過(guò)對(duì)解析域名后得到的 IP 做過(guò)濾,禁止訪問(wèn)內(nèi)部 IP 地址來(lái)達(dá)到防范 SSRF 攻擊的目的。
框架在 ctx, app 和 agent 上都提供了 safeCurl 方法,在發(fā)起網(wǎng)絡(luò)請(qǐng)求的同時(shí)會(huì)對(duì)指定的內(nèi)網(wǎng) IP 地址過(guò)濾,除此之外,該方法和框架提供的 curl 方法一致。
直接調(diào)用 safeCurl 方法其實(shí)并沒(méi)有任何作用,還需要配合安全配置項(xiàng)。
// config/config.default.js |
是否為安全域名。安全域名在配置中配置,見(jiàn) ctx.redirect 部分。
這個(gè)函數(shù)提供了模板預(yù)處理-自動(dòng)插入 CSRF key 的能力,可以自動(dòng)在所有的 form 標(biāo)簽中插入 CSRF 隱藏域,用戶就不需要手動(dòng)寫(xiě)了。
這個(gè)函數(shù)提供了模板預(yù)處理-自動(dòng)插入 nonce 的能力,如果網(wǎng)站開(kāi)啟了 CSP 安全頭,并且想使用 CSP 2.0 nonce 特性,可以使用這個(gè)函數(shù)。參考 CSP 是什么。
這個(gè)函數(shù)會(huì)掃描模板中的 script 標(biāo)簽,并自動(dòng)加上 nonce 頭。
對(duì)于沒(méi)有開(kāi)啟 HTTPS 的網(wǎng)站,這個(gè)函數(shù)可以有限的防止運(yùn)營(yíng)商劫持。
更多建議: