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