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