前後端分離的思考與實踐(四)

發表於2014-06-20

前後端分離模式下的安全解決方案

前言

在前後端分離的開發模式中,從開發的角色和職能上來講,一個最明顯的變化就是:以往傳統中,只負責瀏覽器環境中開發的前端同學,需要涉獵到服務端層面,編寫服務端程式碼。而擺在面前的一個基礎性問題就是如何保障Web安全?

本文就在前後端分離模式的架構下,針對前端在Web開發中,所遇到的安全問題以及應對措施和注意事項,並提出解決方案。

跨站指令碼攻擊(XSS)的防禦

問題及解決思路

跨站指令碼攻擊(XSS,Cross-site scripting)是最常見和基本的攻擊Web網站的方法。攻擊者可以在網頁上釋出包含攻擊性程式碼的資料,當瀏覽者看到此網頁時,特定的指令碼就會以瀏覽者使用者的身份和許可權來執行。通過XSS可以比較容易地修改使用者資料、竊取使用者資訊以及造成其它型別的攻擊,例如:CSRF攻擊。

預防XSS攻擊的基本方法是:確保任何被輸出到HTML頁面中的資料以HTML的方式進行轉義(HTML escape)。例如下面的模板程式碼:

這段程式碼中的$description為模板的變數(不同模板中定義的變數語法不同,這裡只是示意一下),由使用者提交的資料,那麼攻擊者可以輸入一段包含”JavaScript”的程式碼,使得上述模板語句的結果變成如下的結果:

上述程式碼,在瀏覽器中渲染,將會執行JavaScript程式碼並在螢幕上alert hello。當然這個程式碼是無害的,但攻擊者完全可以建立一個JavaScript來修改使用者資料或者竊取cookie資料。

解決方法很簡單,就是將$description的值進行html escape,轉義後的輸出程式碼如下

以上經過轉義後的HTML程式碼是沒有任何危害的。

Midway的解決方案

轉義頁面中所有使用者輸出的資料

對資料進行轉義有以下幾種情況和方法:

1. 使用模板內部提供的機制進行轉義

中途島內部使用KISSY xtemplate作為模板語言。

在xtemplate實現中,語法上使用兩個中括號( {{val}})解析模板資料, ,預設既是對資料進行HTML轉義的,所以開發者可以這樣寫模板:

在xtemplate中,如果不希望輸出的資料被轉義,需要使用三個中括號({{{val}}})。

2. 在Midway中明確的呼叫轉義函式

開發者可以在Node.js程式或者模板中,直接呼叫Midway提供的HTML轉義方法,顯示的對資料進行轉義,如下:

方法1:在Node.js程式中對資料進行HTML轉義

方法2:在模板中對HTML資料進行HTML轉義

注意:只有當模板內部沒有對資料進行轉義的時候才使用Security.escapeHtml進行轉義。 否則,模板內部和程式會兩次轉義疊加,導致不符合預期的輸出。

推薦:如果使用xtemplate,建議直接使用模板內建的{{}}進行轉義; 如果使用其他模板,建議使用Security.escapeHtml進行轉義。

過濾頁面中使用者輸出的富文字

你可能會想到:“其實我就是想輸出富文字,比如一些留言板、論壇給使用者提供一些簡單的字型大小、顏色、背景等功能,那麼我該如何處理這樣的富文字來防止XSS呢?”

1. 使用Midway中Security提供的richText函式

Midway中提供了richText方法,專門用來過濾富文字,防止XSS、釣魚、cookie竊取等漏洞。

有一個留言板,模板程式碼可能如下:

因為message是使用者的輸入資料,其留言板的內容,包含了富文字資訊,所以這裡在xtemplate中,使用了三個大括號,預設不進行HTML轉義;那麼使用者輸入的資料假如如下:

上述的富文字資料如果直接輸出到頁面中,必然會導致eval.com站點的js注入到當前頁面中,造成了XSS攻擊。為了防止這個漏洞,我們只要在模板或者程式中,呼叫Security.richText方法,處理使用者輸入的富文字。

呼叫方法與escapeHtml類似,有如下兩種方式

方法1: 直接在Node.js程式中呼叫

方法2: 在模板中呼叫

通過呼叫Security的richText方法後,最終的輸出如下:

可以看出,首先:會造成XSS攻擊的script標籤被直接過濾掉;同時style標籤中CSS屬性position:fixed;樣式也被過濾了。最終輸出了無害的HTML富文字

瞭解其他可能導致XSS攻擊的途徑

除了在頁面的模板中可能存在XSS攻擊之外,在Web應用中還有其他幾個途徑也可能會有風險。

1. 出錯頁面的漏洞

一個頁面如果找不到,系統可能會報一個404 Not Found的錯誤,例如:http://localhost/page/not/found

很顯然:攻擊者可以利用這個頁面,構造一個類似這樣的連線,http://localhost/%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E,並引誘受害者點選 ;假如出錯頁面未對輸出變數進行轉義的話,那麼連線中隱藏的 <script>alert('hello')</script> 將會被執行。

在express中,傳送一個404頁面的方法如下

這裡就需要開發者注意錯誤頁面(404或者其他錯誤狀態)的處理方式。如果錯誤資訊的返回內容帶有路徑資訊(其實更準確的講,是使用者輸入資訊),就一定要進行escapeHtml了。

後續,錯誤處理的安全機制,會在Midway框架層面中完成。

Midway解決方案的補充說明

其他模板引擎

Midway預設支援xtemplate模板,但將來也有可能支援其他模板:如jade、mustache、ejs等。目前在主流模板中,都提供了預設轉義和不轉義的輸出變數寫法,需要開發者特別留意其安全性。

關於escape的其他支援

除了對頁面中輸出的普通資料和富文字資料,一些場景中也還包含其他可能需要轉義的情況,Midway提供瞭如下幾個常用的轉義方法,供開發者使用:

  • escapeHtml 過濾指定的HTML中的字元,防XSS漏洞
  • jsEncode 對輸入的String進行JavaScript 轉義 對中文進行unicode轉義,單引號,雙引號轉義
  • escapeJson 不破壞JSON結構的escape函式,只對json結構中name和vaule做escapeHtml處理
  • escapeJsonForJsVar 可以理解就是jsEncode+escapeJson

例子如下

跨站請求偽造攻擊(CSRF)的預防

問題及解決思路

名詞解釋: 表單:泛指瀏覽器端用於客戶端提交資料的形式;包括a標籤、ajax提交資料、form表單提交資料等,而非對等於HTML中的form標籤。

跨站請求偽造(CSRF,Cross-site request forgery)是另一種常見的攻擊。攻擊者通過各種方法偽造一個請求,模仿使用者提交表單的行為,從而達到修改使用者的資料或執行特定任務的目的。

為了假冒使用者的身份,CSRF攻擊常常和XSS攻擊配合起來做,但也可以通過其它手段:例如誘使使用者點選一個包含攻擊的連結。

解決CSRF攻擊的思路分如下兩個步驟

  1. 增加攻擊的難度。GET請求是很容易建立的,使用者點選一個連結就可以發起GET型別的請求,而POST請求相對比較難,攻擊者往往需要藉助JavaScript才能實現;因此,確保form表單或者服務端介面只接受POST型別的提交請求,可以增加系統的安全性。
  2. 對請求進行認證,確保該請求確實是使用者本人填寫表單或者發起請求並提交的,而不是第三者偽造的。

一個正常使用者修改網站資訊的過程如下

  • 使用者請求修改資訊(1) -> 網站顯示使用者修改資訊的表單(2) -> 使用者修改資訊並提交(3) -> 網站接受使用者修改的資料並儲存(4)

而一個CSRF攻擊則不會走這條路線,而是直接偽造第2步使用者提交資訊

  • 直接跳到第2步(1) -> 偽造要修改的資訊並提交(2) -> 網站接受攻擊者修改引數資料並儲存(3)

只要能夠區分這兩種情況,就能夠預防CSRF攻擊。那麼如何區分呢? 就是對第2步所提交的資訊進行驗證,確保資料來源自第一步的表單。具體的驗證過程如下:

  • 使用者請求修改資訊(1) -> 網站顯示用於修改資訊的空白表單,表單中包含特殊的token同時把token儲存在session中(2) -> 使用者修改資訊並提交,同時發回token資訊到服務端(3) -> 網站比對使用者發回的token和session中的token,應該一致,則接受使用者修改的資料,並儲存

這樣,如果攻擊者偽造要修改的資訊並提交,是沒辦法直接訪問到session的,所以也沒辦法拿到實際的token值;請求傳送到服務端,服務端進行token校驗的時候,發現不一致,則直接拒絕此次請求。

Midway解決方案

禁用GET提交表單

如果服務端不接受GET方式提交的表單資料,那麼將會給攻擊者帶來非常大的難度;因為在頁面上構造一個a標籤href屬性或者img標籤src屬性來構造一個請求是非常容易的,但是如果要POST提交,就必須要通過指令碼才可以實現。

用CSRF token驗證請求

因為Midway不涉及到淘寶分散式session及token校驗這一層面邏輯,所以在Midway框架中,只將token在server和客戶端之間進行轉發,本身不做實際的校驗工作。流程如下:

T13dy0FNhcXXc3iaIJ-1432-994

後續:在Midway中,Node.js和淘寶的分散式session對接後,可以考慮在Midway這一層自動進行token校驗;畢竟安全校驗越早進行,成本也會更低。

建議:在Midway中,可以判斷是否request中有token的值,如果一個修改操作,沒有token,可以直接在Midway層認為是不安全的,將請求丟棄掉。

其他安全問題

關於常見的Web安全問題,還有如下幾種,這裡只做一些簡介,後續會持續繼承到Midway framework中。

  • HTTP Headers安全
    • CRLF Injection 攻擊者想辦法在響應頭中注入兩個CRLF特殊字元,導致響應資料格式異常,從而注入script等
    • 拒絕訪問攻擊 每個請求因為都會預設帶上cookie,而伺服器一般都會限制cookie的大小,這就導致了,如果使用者客戶端cookie被設定成了超過某個閥值,那麼使用者就再也無法訪問網站了
    • cookie防竊取 一般cookie竊取都是通過JavaScript(XSS漏洞)獲取到的,所以儘量將cookie設定成http only,並且加上cookie過期時間

關於cookie的安全問題,之前WebX已經有較好的解決方案;此次Midway不負責cookie的設定和校驗等工作,只負責轉發到WebX層面進行check

關於Node.js

XSS等注入性漏洞是所有漏洞中最容易被忽略,佔網際網路總攻擊的70%以上;開發者編寫Node.js程式碼時,要時刻提醒自己,永遠不要相信使用者的輸入。

比如如下幾個例子。

  • var mod = fs.readFileSync('path'); 如果path來源於使用者輸入,那麼假設使用者輸入/etc/password,則會讀取到不應該讀取的內容,造成密碼洩漏風險
  • var result = eval(jsonVal); 一定要確保jsonVal是json,而不是使用者的輸入
  • …… 其他可能包含使用者輸入的地方,一定要確認使用者的輸入是我們期望的值

總結

前後端分離模式下,可以讓傳統的前端開發人員開始編寫後端程式碼,雖然從架構上講,只負責模板這一層,但也會接觸大量的後端程式碼;所以安全對於前端來說,這是一個不小的挑戰。

【附】相關文章列表

1. 《前後端分離的思考與實踐(一)》

2. 《前後端分離的思考與實踐(二)》

3. 《前後端分離的思考與實踐(三)》

 

 

 

相關文章