原文出處: 淘寶UED - lorrylockie html
先後端分離模式下的安全解決方案
前言
在先後端分離的開發模式中,從開發的角色和職能上來說,一個最明顯的變化就是:以往傳統中,只負責瀏覽器環境中開發的前端同窗,須要涉獵到服務端層面,編寫服務端代碼。而擺在面前的一個基礎性問題就是如何保障Web安全?前端
本文就在先後端分離模式的架構下,針對前端在Web開發中,所遇到的安全問題以及應對措施和注意事項,並提出解決方案。node
跨站腳本攻擊(XSS,Cross-site scripting)是最多見和基本的攻擊Web網站的方法。攻擊者能夠在網頁上發佈包含攻擊性代碼的數據,當瀏覽者看到此網頁時,特定的腳本就會以瀏覽者用戶的身份和權限來執行。經過XSS能夠比較容易地修改用戶數據、竊取用戶信息以及形成其它類型的攻擊,例如:CSRF攻擊。git
預防XSS攻擊的基本方法是:確保任何被輸出到HTML頁面中的數據以HTML的方式進行轉義(HTML escape)。例以下面的模板代碼:github
1
|
<textarea name="description">$description</textarea>
|
這段代碼中的$description
爲模板的變量(不一樣模板中定義的變量語法不一樣,這裏只是示意一下),由用戶提交的數據,那麼攻擊者能夠輸入一段包含」JavaScript」的代碼,使得上述模板語句的結果變成以下的結果:ajax
1
2
3
|
<textarea name="description">
</textarea><script>alert('hello')'</script>
</textarea>
|
上述代碼,在瀏覽器中渲染,將會執行JavaScript代碼並在屏幕上alert hello。固然這個代碼是無害的,但攻擊者徹底能夠建立一個JavaScript來修改用戶資料或者竊取cookie數據。express
解決方法很簡單,就是將$description
的值進行html escape,轉義後的輸出代碼以下json
1
2
3
|
<textarea name="description">
</textarea><script>alert("hello!")</script>
</textarea>
|
以上通過轉義後的HTML代碼是沒有任何危害的。後端
對數據進行轉義有如下幾種狀況和方法:瀏覽器
中途島內部使用KISSY xtemplate做爲模板語言。
在xtemplate實現中,語法上使用兩個中括號( {{val}}
)解析模板數據, ,默認既是對數據進行HTML轉義的,因此開發者能夠這樣寫模板:
1
|
<textarea name="description">{{description}}</textarea>
|
在xtemplate中,若是不但願輸出的數據被轉義,須要使用三個中括號({{{val}}}
)。
開發者能夠在Node.js程序或者模板中,直接調用Midway提供的HTML轉義方法,顯示的對數據進行轉義,以下:
方法1:在Node.js程序中對數據進行HTML轉義
1
2
3
4
|
var
Security= require('midway-security');
//data from server,eg {html:'</textarea>',other:""}
data
.html =Security.escapeHtml(data.html);
xtpl
= xtpl.render(data);
|
方法2:在模板中對HTML數據進行HTML轉義
1
|
<textarea name="description">Security.escapeHtml({{{description}}})</textarea>
|
注意:只有當模板內部沒有對數據進行轉義的時候才使用Security.escapeHtml進行轉義。 不然,模板內部和程序會兩次轉義疊加,致使不符合預期的輸出。
推薦:若是使用xtemplate,建議直接使用模板內置的{{}}
進行轉義; 若是使用其餘模板,建議使用Security.escapeHtml
進行轉義。
你可能會想到:「其實我就是想輸出富文本,好比一些留言板、論壇給用戶提供一些簡單的字體大小、顏色、背景等功能,那麼我該如何處理這樣的富文原本防止XSS呢?」
Midway中提供了richText方法,專門用來過濾富文本,防止XSS、釣魚、cookie竊取等漏洞。
有一個留言板,模板代碼可能以下:
1
2
3
|
<div class="message-board">
{{{message}}}
</div>
|
由於message是用戶的輸入數據,其留言板的內容,包含了富文本信息,因此這裏在xtemplate中,使用了三個大括號,默認不進行HTML轉義;那麼用戶輸入的數據假如以下:
1
|
<script src="http://eval.com/eval.js"></script><span style="color:red;font-size:20px;position:fixed;">我在留言中</span>
|
上述的富文本數據若是直接輸出到頁面中,必然會致使eval.com站點的js注入到當前頁面中,形成了XSS攻擊。爲了防止這個漏洞,咱們只要在模板或者程序中,調用Security.richText方法,處理用戶輸入的富文本。
調用方法與escapeHtml相似,有以下兩種方式
方法1: 直接在Node.js程序中調用
1
2
|
message
=Security.richText(message);
var
html = xtpl.render(message)
|
方法2: 在模板中調用
1
2
3
|
<div class="message-board">
Security.richText({{{message}}})
</div>
|
經過調用Security的richText方法後,最終的輸出以下:
1
2
3
|
<div class="message-board">
<span style="color:red;font-size:20px;">我在留言中</span>
</div>
|
能夠看出,首先:會形成XSS攻擊的script
標籤被直接過濾掉;同時style標籤中CSS屬性position:fixed;
樣式也被過濾了。最終輸出了無害的HTML富文本
除了在頁面的模板中可能存在XSS攻擊以外,在Web應用中還有其餘幾個途徑也可能會有風險。
一個頁面若是找不到,系統可能會報一個404 Not Found的錯誤,例如:http://localhost/page/not/found
1
2
|
404
NotFound
Page
/page/not/found does not exsit
|
很顯然:攻擊者能夠利用這個頁面,構造一個相似這樣的鏈接,http://localhost/%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E
,並引誘受害者點擊 ;假如出錯頁面未對輸出變量進行轉義的話,那麼鏈接中隱藏的 <script>alert('hello')</script>
將會被執行。
在express中,發送一個404頁面的方法以下
1
|
res
.send(404,'Sorry,we don\'t find that!')
|
這裏就須要開發者注意錯誤頁面(404或者其餘錯誤狀態)的處理方式。若是錯誤信息的返回內容帶有路徑信息(其實更準確的講,是用戶輸入信息),就必定要進行escapeHtml了。
後續,錯誤處理的安全機制,會在Midway框架層面中完成。
Midway默認支持xtemplate模板,但未來也有可能支持其餘模板:如jade、mustache、ejs等。目前在主流模板中,都提供了默認轉義和不轉義的輸出變量寫法,須要開發者特別留意其安全性。
除了對頁面中輸出的普通數據和富文本數據,一些場景中也還包含其餘可能須要轉義的狀況,Midway提供了以下幾個經常使用的轉義方法,供開發者使用:
例子以下
1
2
3
4
5
6
|
var
jsonText ="{\"<script>\":\"<script>\"}";
console
.log(SecurityUtil.escapeJson(jsonText));// {"<script>":"<script>"}
var
jsonText ="{\"你好\":\"<script>\"}";
console
.log(SecurityUtil.escapeJsonForJsVar(jsonText));//{\"\u4f60\u597d\":\"<script>\"}
var
str ="alert(\"你好\")";
console
.log(SecurityUtil.jsEncode(str));// alert(\"\u4f60\u597d\")
|
名詞解釋: 表單:泛指瀏覽器端用於客戶端提交數據的形式;包括a標籤、ajax提交數據、form表單提交數據等,而非對等於HTML中的form標籤。
跨站請求僞造(CSRF,Cross-site request forgery)是另外一種常見的攻擊。攻擊者經過各類方法僞造一個請求,模仿用戶提交表單的行爲,從而達到修改用戶的數據或執行特定任務的目的。
爲了假冒用戶的身份,CSRF攻擊經常和XSS攻擊配合起來作,但也能夠經過其它手段:例如誘使用戶點擊一個包含攻擊的連接。
解決CSRF攻擊的思路分以下兩個步驟
一個正經常使用戶修改網站信息的過程以下
而一個CSRF攻擊則不會走這條路線,而是直接僞造第2步用戶提交信息
只要可以區分這兩種狀況,就可以預防CSRF攻擊。那麼如何區分呢? 就是對第2步所提交的信息進行驗證,確保數據源自第一步的表單。具體的驗證過程以下:
這樣,若是攻擊者僞造要修改的信息並提交,是沒辦法直接訪問到session的,因此也沒辦法拿到實際的token值;請求發送到服務端,服務端進行token校驗的時候,發現不一致,則直接拒絕這次請求。
若是服務端不接受GET方式提交的表單數據,那麼將會給攻擊者帶來很是大的難度;由於在頁面上構造一個a標籤href屬性或者img標籤src屬性來構造一個請求是很是容易的,可是若是要POST提交,就必需要經過腳本才能夠實現。
由於Midway不涉及到淘寶分佈式session及token校驗這一層面邏輯,因此在Midway框架中,只將token在server和客戶端之間進行轉發,自己不作實際的校驗工做。流程以下:
後續:在Midway中,Node.js和淘寶的分佈式session對接後,能夠考慮在Midway這一層自動進行token校驗;畢竟安全校驗越早進行,成本也會更低。
建議:在Midway中,能夠判斷是否request中有token的值,若是一個修改操做,沒有token,能夠直接在Midway層認爲是不安全的,將請求丟棄掉。
關於常見的Web安全問題,還有以下幾種,這裏只作一些簡介,後續會持續繼承到Midway framework中。
關於cookie的安全問題,以前WebX已經有較好的解決方案;這次Midway不負責cookie的設置和校驗等工做,只負責轉發到WebX層面進行check
XSS等注入性漏洞是全部漏洞中最容易被忽略,佔互聯網總攻擊的70%以上;開發者編寫Node.js代碼時,要時刻提醒本身,永遠不要相信用戶的輸入。
好比以下幾個例子。
var mod = fs.readFileSync('path');
若是path來源於用戶輸入,那麼假設用戶輸入/etc/password
,則會讀取到不該該讀取的內容,形成密碼泄漏風險var result = eval(jsonVal);
必定要確保jsonVal是json,而不是用戶的輸入先後端分離模式下,可讓傳統的前端開發人員開始編寫後端代碼,雖然從架構上講,只負責模板這一層,但也會接觸大量的後端代碼;因此安全對於前端來講,這是一個不小的挑戰。