RESTful 架構風格下的 4 大常見安全問題

ThoughtWorks發表於2017-01-05

伴隨著RESTful架構風格的大量應用微服務架構的流行,一些本來難以察覺到的安全問題也逐漸開始顯現出來。在我經歷過的各種採用RESTful微服務架構風格的應用中,某些安全問題幾乎在每個應用中都會出現。然而它們並非是什麼高深的技術難題,只不過是藉著微服務的流行而顯得越發突出,這些都可以通過一些安全實踐來避免。本文將一些典型的問題列舉出來,希望能引起開發團隊的注意,幫助他們繞過這些安全問題的“坑”。

1. 遺漏了對資源從屬關係的檢查

一個典型的RESTful的URL會用資源名加上資源的ID編號來標識其唯一性,就像這樣:/users/,例如:/users/100

一般而言使用者只能檢視自己的使用者資訊,而不允許檢視其它使用者的資訊。在這種情況下,攻擊者很可能會嘗試把這個URL裡面的USER ID從100修改為其他數值,以期望應用返回指定使用者的資訊。不過由於這個安全風險太顯而易見,絕大多數應用都會對當前請求者的身份進行校驗,看其是否是編號為100的使用者,校驗成功才返回URL中指定的使用者資訊,否則會拒絕當前請求。

對於URL中只出現一個資源的情況,絕大多數應用都已經做了安全防禦,然而重災區出現在URL中包含多個資源的時候。

以使用者檢視訂單的RESTful URL為例:/users/100/orders/280010,應用只檢查了當前請求發起者是否是編號為100的使用者,以及編號為280010的訂單是否存在,有很大的概率沒有檢查URL中的訂單和使用者之間的從屬關係。其結果是,攻擊者可以通過修改URL中的訂單編號,從而遍歷系統中的所有訂單資訊,甚至對不屬於他/她的訂單發起操作,例如取消訂單。

上面的例子中只有兩個資源,如果URL中資源數量繼續增加,這種從屬關係校驗缺失的情況只會更加普遍。

解決這一問題的方法極其簡單,只要發現URL裡面出現了兩個或者兩個以上的資源,就像下面這樣:

/ResourceA//ResourceB//ResourceC/

在對資源進行操作之前,就得先檢查這些資源之間的從屬關係,以確保當前請求具有相關的訪問、操作許可權。

2. HTTP響應中缺失必要的 Security Headers

HTTP中有一些和安全相關的Header,通過對它們的合理使用,可以使得應用在具備更高的安全性的同時,並不會顯著增大開發者的工作負擔,有著“低成本高收益”的效果。不過絕大多數情況下,這些Header是預設關閉的,因此很多應用中也就缺失了這些Security Headers。一些典型的Security Headers如下:

X-Frame-Options
為了防止應用遭受點選劫持攻擊,可以使用X-Frame-Options: DENY明確告知瀏覽器,不要把當前HTTP響應中的內容在HTML Frame中顯示出來。

X-Content-Type-Options
在瀏覽器收到HTTP響應內容時,它會嘗試按照自己的規則去推斷響應內容的型別,並根據推斷結果執行後續操作,而這可能造成安全問題。例如,一個包含惡意JavaScript程式碼的HTTP響應內容,雖然其Content-Typeimage/png,但是瀏覽器推斷出這是一段指令碼並且會執行它。

X-Content-Type-Options就是專門用來解決這個問題的Header。通過將其設定為X-Content-Type-Options: nosniff,瀏覽器將不再自作主張的推斷HTTP響應內容的型別,而是嚴格按照響應中Content-Type所指定的型別來解析響應內容。

X-XSS-Protection
避免應用出現跨站指令碼漏洞(Cross-Site Scripting,簡稱XSS)的最佳辦法是對輸出資料進行正確的編碼,不過除此之外,現如今的瀏覽器也自帶了防禦XSS的能力。

要開啟瀏覽器的防XSS功能,只需要在HTTP響應中加上這個Header:X-XSS-Protection: 1; mode=block。其中,數字1代表開啟瀏覽器的XSS防禦功能,mode=block是告訴瀏覽器,如果發現有XSS攻擊,則直接遮蔽掉當前即將渲染的內容。

Strict-Transport-Security
使用TLS可以保護資料在傳輸過程中的安全,而在HTTP響應中新增上Strict-Transport-Security這個Header,可以告知瀏覽器直接發起HTTPS請求,而不再像往常那樣,先傳送明文的HTTP請求,得到伺服器跳轉指令後再傳送後續的HTTPS請求。並且,一旦瀏覽器接收到這個Header,那麼當它發現資料傳輸通道不安全的時候,它會直接拒絕進行任何的資料傳輸,不再允許使用者繼續通過不安全的傳輸通道傳輸資料,以避免資訊洩露。

3. 不經意間洩露的業務資訊

會說話的ID
資源ID是RESTful URL中很重要的一個組成部分,大多數情況下這類資源ID都是用數字來表示的。這在不經意間洩露了業務資訊,而這些資訊可能正是競爭對手希望得到的資料。

以檢視使用者資訊的RESTful URL為例:/users/100。由於使用者ID是一個按序遞增的數字,因此攻擊者既可以通過ID知道目前應用中的使用者規模,也可以分別在月初和月末的時候註冊一個使用者,並對比兩個使用者的ID即可知道當前這個月有多少新增使用者。同理,如果訂單號也是按序自增的數字,攻擊者可以瞭解到一定時間範圍內的訂單量。

這類ID並不會給應用造成任何技術上的威脅,只是通過ID洩露出來的資訊對於你的業務而言可能非常敏感。解決辦法是不使用按序遞增的數字作為ID,而是使用具有隨機性、唯一性、不可預測性的值作為ID,最常見的做法就是使用UUID。

返回多餘的資料
前後端分離的情況下,兩者之間通常以JSON作為資料傳輸的主體。有時候可能是為了方便前端程式碼處理,也可能是疏忽大意,總之後端API返回的JSON資料中包含了遠遠超出前端程式碼需要的資料,因此造成資料洩露。

例如,前端程式碼本意是請求訂單資訊,但是後端API返回的訂單JSON資料中還包含了很多“有意思”的資料。

上面這個例子裡,訂單資料中包含了使用者資訊,最為關鍵的是連使用者的密碼欄位也被包含在內。

解決辦法顯而易見,在給前端返回資料之前,將這些敏感的、前端並不需要的資料過濾掉。技術上實現起來易如反掌,但是真正難的地方在於讓整個應用都嚴格的按照這樣的方式來處理JSON資料,確保沒有任何遺漏之處。

4. API缺乏速率限制的保護

先看一個例子。使用者註冊時傳送簡訊驗證碼的API,由於沒有做速率限制,使得攻擊者可以用一段指令碼不斷的請求伺服器傳送簡訊驗證碼,導致在短時間內耗盡簡訊傳送配額,或者造成簡訊閘道器擁擠等等後果。

受傷的不僅僅是傳送簡訊的API,其他一些比較敏感的API如果缺乏請求速率限制的保護,同樣也會遭遇安全問題。例如使用者登入的API缺乏速率限制的話,攻擊者可以利用其進行使用者名稱密碼暴力破解,再例如某些大量消耗伺服器資源的API如果缺乏速率限制,攻擊者可以利用其發起拒絕式攻擊。

解決這類安全問題的原則就是對API請求的速率進行適當的限制。具體的做法有很多,最典型的例子就是使用圖片驗證碼,其他的做法還有利用Redis的Expire特性對請求速率進行統計判斷,甚至藉助運維的力量(例如網路防火牆)來共同進行防禦等等。

總結

開發出一個具備足夠安全性的應用不是件容易的事情,本文中提到的只是RESTful架構風格下,眾多安全問題中比較典型的一部分而已。之所以會有這些問題,其本質原因在於應用開發過程中,開發團隊的注意力集中在業務功能的實現上,應用安全性相關的需求沒有得到足夠的明確和重視。

如果你不想被這些安全問題所困擾,建議通過在應用開發過程中引入威脅建模、在使用者故事卡中設立安全驗收標準、進行安全程式碼審查等一系列安全實踐,儘可能從源頭上規避這些問題。

相關文章