如何設計好的RESTful API之安全性

原文:http://blog.csdn.net/ywk253100/article/details/25654101算法

導讀:安全是恆久的話題,對於基於WSDL和SOAP的Web Service,咱們有WS-Security這樣的安全規範來指導實現認證、受權、身份管理等安全需求。如何保證RESTful API的安全性呢。編程

關鍵詞:RESTful API API安全性 瀏覽器

前面講了好的RESTful API具備的一些特徵,本文會繼續探討RESTful API的安全性問題。七牛雲存儲

InfoQ:安全是恆久的話題,對於基於WSDL和SOAP的Web Service,咱們有WS-Security這樣的安全規範來指導實現認證、受權、身份管理等安全需求。那麼,RESTful API有無成熟可用規範或實現框架呢?如何保證RESTful API的安全性呢?安全

李錕:保證RESTful API的安全性,主要包括三大方面:
a) 對客戶端作身份認證
b) 對敏感的數據作加密,而且防止篡改
c) 身份認證以後的受權
對客戶端作身份認證,有幾種常見的作法:
在請求中加簽名參數 服務器

1.爲每一個接入方分配一個密鑰,而且規定一種簽名的計算方法。要求接入方的請求中必須加上簽名參數。這個作法是最簡單的,可是須要確保接入方密鑰的 安全保存,另外還要注意防範replay攻擊。其優勢是容易理解與實現,缺點是須要承擔安全保存密鑰和按期更新密鑰的負擔,並且不夠靈活,更新密鑰和升級 簽名算法很困難。 網絡

使用標準的HTTP身份認證機制 架構

HTTP Basic身份認證安全性較低,必須與HTTPS配合使用。HTTP Digest身份認證能夠單獨使用,具有中等程度的安全性。 框架

HTTP Digest身份認證機制還支持插入用戶自定義的加密算法,這樣能夠進一步提升API的安全性。不過插入自定義加密算法在面向互聯網的API中用的不是不少。
這個作法須要確保接入方「安全域-用戶名-密碼」三元組信息的安全保存,另外還要注意防範replay攻擊。 異步

優勢:基於標準,獲得了普遍的支持(大量HTTP服務器端、客戶端庫)。在服務器端作HTTP身份認證的職責能夠由Web Server(例如 Nginx)、App Server(例如Tomcat)、安全框架(例如Spring Security)來承擔,對應用開發者來講是透明的。HTTP 身份認證機制(RFC 2617)很是好地體現了「分離關注點」的設計原則,並且保持了操做語義的可見性。

2.缺點:這類基於簡單用戶名+密碼機制的安全性不可能高於基於非對稱密鑰的機制(例如數字證書)。

使用OAuth協議作身份認證

OAuth協議適用於爲外部應用受權訪問本站資源的狀況。其中的加密機制與HTTP Digest身份認證相比,安全性更高。須要注意,OAuth 身份認證與HTTP Digest身份認證之間並非相互取代的關係,它們的適用場景是不一樣的。OAuth協議更適合於爲面向最終用戶維度的API提供授 權,例如獲取隸屬於用戶的微博信息等等。若是API並非面向最終用戶維度的,例如像七牛雲存儲這樣的存儲服務,這並不是是OAuth協議的典型適用場景。
3.對敏感的數據作加密,而且防止篡改,常見的作法有:

部署SSL基礎設施(即HTTPS),敏感數據的傳輸所有基於SSL。 
僅對部分敏感數據作加密(例如預付費卡的卡號+密碼),並加入某種隨機數做爲加密鹽,以防範數據被篡改。
 
身份認證以後的受權,主要是由應用來控制。一般應該實現某種基於角色+用戶組的受權機制,這方面的框架有很多(例如Spring Security),不過大多數開發團隊仍是喜歡本身來實現相關功能。

李建業:我不認爲安全是RESTful API須要考慮的問題,事實上我以爲這是兩個正交的問題。固然,若是使用RESTful API來提供認證、受權和身份管理,那也算是雙方有關係,可是這和其它風格的API設計所要考慮的問題彷佛沒什麼區別,不值得特別注意。

可是在具體設計層面,這二者的「正交點」上彷佛確實有些問題,由於REST是一個推崇狀態無關原則的架構風格,而認證和受權一般基於第三方解決方案,因此每每會出現違背有狀態約束的問題,這個地方我也沒有特別的想法,固然這個困難和原問題關係不大。
至於WS-族的協議,我不太瞭解,不太能參與討論。

丁雪豐:對於RESTful API,常見的安全措施都是能夠繼續使用的。例如,爲了防篡改,能夠對所有參數進 行簽名;爲了防範重放攻擊能夠在請求中增長一次性的Token,或者短期內有效的Token;對內容加密能夠實現數據防泄露……;對於DDoS攻擊,各 種HTTP流量清洗策略,均可以繼續發揮做用,由於這就是基本的HTTP請求。

在受權和認證方面,OAuth 2.0已經基本成熟了,而且獲得了普遍地應用。若是能夠,接入第三方帳戶體系是個不錯的選擇,好比Google和Facebook的,國內的固然也有幾個候選。

馬鈞:我的認爲RESTful的安全性分爲幾個層次,在安全要求較高的場合,能夠經過HTTPs這樣的加密協議來保證網絡層的安全,應用層的安全能夠經過OAuth實現認證,而對於資源的訪問受權,則只能依靠應用程序來實現了。

InfoQ:如何對RESTful API進行版本控制,請分享您認爲實用的作法?

李錕:一個比較簡單實用的作法是直接在URI中插入版本號,這樣作容許多個版本的API並行運行。

另外一個作法是在HTTP請求中加入自定義頭信息,標明使用的版本號。不過這個作法其實對瀏覽器不夠友好,簡單地使用瀏覽器+HTML沒法測試。

李建業:目前比較好的方式仍是在uri設計中添加版本信息,其它方法都不如這個實用。

丁雪豐:我的認爲最好的版本化,就是沒有明顯的版本。在對已發佈的服務進行變動時,要儘可能作到兼容,其中包括 URI、連接和各類不一樣的表述的兼容,最關鍵的就是在擴展時不能破壞現有的客戶端。例如,要變動一個參數,能夠選擇同時兼容新舊兩種輸入,或者保持老參數 不動,提供一個新的參數,在文檔中必須作出說明,不推薦新用戶再繼續使用以前的參數。

若是必需要進行不兼容的變動,那麼能夠選擇標記不一樣的版本號,這時能夠選擇在路徑或參數中增長版本信息。也有作法是增長HTTP標頭,只是在調用時會稍有不便,推薦前兩種方法。

馬鈞:RESTfulAPI 的版本升級,儘可能兼容以前的版本,保證原有的API都能正常工做,能夠經過HTTP 301轉跳到新的資源。另一種實用的作法就是在url中保留版本 號,同時提供多個版本供客戶端使用,如 v1.rest.com 或者 rest.com/v1/ 這樣。

InfoQ:HTTP1.1規範中給出的動詞對於設計RESTful API夠用嗎?您在實際項目中會擴展本身的動詞嗎?在什麼狀況下須要擴展?

李錕:這個問題取決於設計者如何看待和設計資源。若是資源抽象作的很好,對於某個資源的任何操做,一般都可以映 射到CRUD四個類別中。CRUD四個類別對於操做資源來講,絕大多數狀況下是完備的。HTTP的GET/POST/PUT/DELETE四個方法,對於 CRUD四個類別的操做來講是足夠的,映射關係是Create-POST/Retrieve-GET/Update-PUT/Delete- DELETE。

咱們一般不會選擇建立本身的動詞,這樣作對於客戶端開發者來講,須要更多的學習成本。若是在資源上定義的操做過多,咱們會選擇拆分出更多的資源。

李建業:一 般是夠用的,有時一些「不夠用」的場景是因爲咱們沒有設計出合理的資源,好比批量操做。可是,正如以前所說的那樣,對於某些內部的、傳統的(所以模型穩定 且已知)系統,API提供者和調用者會有自已的固定動詞表,此時不必拘泥。另外,我不建議擴展動詞,一旦擴展了動詞,其實已經破壞了我以前說的*「儘可 能少的先驗信息」*,那麼,擴展動詞和從新設計動詞的成本差異不大。基於這個考慮,我建議儘量保持動詞不變,除非你想從新設計動詞表。
丁雪豐:一 般狀況下,經常使用的HTTP動詞是夠用的,並無出現必定要本身擴展動詞的狀況。其實,最經常使用的也就是GET、POST、DELETE和PUT,而 HEAD、OPTIONS、TRACE則基本用不太到。若是出現一時找不到合適的動詞,安全冪等的操做用GET,其餘均可以用POST,在設計資源時稍加 考慮便可。
馬鈞:在個人實際項目中,只用到了POST,PUT,DELETE,GET這四個動詞。

InfoQ:今年5月份發佈的JAX-RS 2.0規範對於RSTfulAPI的設計最有價值的特性是哪一個(些)? 它(們)用於解決什麼問題?

李錕:REST開發框架RESTEasy項目負責人Bill Burke,去年寫了一篇文章介紹JAX-RS 2.0。

我贊成Bill在文章中的觀點,在JAX-RS 2.0增長的內容中,最重要的三部分爲:

a) Client API——用來規範化JAX-RS客戶端的開發方式。
b) Server-side Asynchronous HTTP——用來實現服務器端推送功能,而不須要依靠低效的輪詢方式。
c) Filters and Interceptors——用來分離關注點,將鑑權、日誌等邏輯與業務邏輯分離開,更好地實現代碼重用。

這三部分的內容對於開發者來講都頗有用。遵循JAX-RS規範作開發,能夠確保服務器端以及客戶端代碼的可移植性。

李建業:我我的關注異步API這部分,主要是由於流式服務將會愈來愈多,那將大量須要這類支持。

InfoQ:可否爲InfoQ的讀者推薦一款實用的RESTful API開發框架,並說明您的推介理由。

李錕:這 個問題我就不詳細回答了。不一樣的編程語言有不一樣的REST開發框架,對於REST的支持程度也不一樣。開發RESTful API的需求範圍很廣,可選擇的 開發框架的範圍也很廣。保持多樣性是繁榮生態環境的基礎。像Java就有支持JAX-RS規範的Jersey、RESTEasy、Restlet、 Apache CXF,和不支持JAX-RS規範的Spring MVC等等不少框架。這些框架目前都作的不錯。我對框架的選擇沒有傾向性。 RESTful API設計的最佳實踐應該是通用的,而不是必須依賴某種特定的開發框架。

李建業:很差意思,這個我不過重視,無法推薦,不過我能夠解釋一下爲何對RESTful API框架不感冒的緣由。

REST做爲一個架構風格,對咱們的系統開發有很大影響,可是這些影響通常是針對架構(例如狀態無關)或者設計(例如資源識別)上的,因此一旦涉及 到具體實現,主要工做就基本結束了,此時開發框架能作的事也就只有簡化編程了(相較而言,有的框架還能起到引導設計的做用),而因爲RESTful會抽象 動詞,因此實現層面中和API規範相關的工做原本就很少,那麼框架的價值就更小了。

固然,咱們也不可能直接基於servlet/rakc/wsgi來開發,不過通常的編程語言都會提供一些簡單的url route/match策 略,咱們使用這些就足夠了。另外,有些框架能幫咱們生成所有的動詞支持,但這也未必是好事,我通常傾向於按需實現——用到了再支持,這就更不須要太關注開 發框架對RESTful的支持了。

丁雪豐:因爲本人是Spring的擁護者,工做中也一直在使用Spring,因此在選擇框架時會更多地傾向 Spring MVC(並非說別的框架很差,這裏有些我的主觀的成份)。若是必定要選擇其餘框架,也要選擇可以方便與Spring集成的框架。若是在項 目中已經使用了Spring,那麼沒有什麼理由不選擇Spring MVC,鑑於目前Spring在各類項目中的高出鏡率,相信通常狀況下都會選擇 Spring MVC。

REST的成熟度模型中,第三層就是HATEOAS,Spring目前還提供了Spring Hateoas子項目,對連接、資源等方面的支持都作了必定的加強。

馬鈞:我目前在實際項目中使用的是Spray,這是一個開源的 REST/HTTP 工具包和底層網絡 IO 包,基於 Scala 和 Akka 構建。輕量級、異步、非堵塞、基於 actor 模式、模塊化和可測試是Spray的特色。

InfoQ:HTTP2.0規範正在制定當中,您對它的期待是什麼?

李錕:個人期待包括兩個方面:應該作的和不該該作的。

HTTP/2.0規範應該作的:

1.與HTTP/1.1協議保持兼容。兼容的含義是說二者能夠並存,客戶端應用能夠根據服務器端的能力,自由地選擇使用HTTP/2.0仍是HTTP/1.1,並且選擇過程對應用來講是透明的。 
2.改進HTTP協議(做爲資源的統一接口)之中操做語義表達方式的語法,提升網絡傳輸效率。 
3.更好地模塊化,這樣HTTP/2.0協議的實現可以更好地模塊化。應用程序可根據須要選擇適當的模塊,而不是要麼全有、要麼全無。 
4.廢棄掉HTTP/1.1協議中一些不多有人用到的部分,例如採用管道(pipelining)方式發送請求。 
5.增長更多的動詞,以適應除CRUD以外的其餘場景。
 
HTTP/2.0規範不該該作的:
HTTP/2.0協議不該該把底層的數據加密機制(即SSL)做爲必選項。
 
HTTP/2.0協議不該該背離REST架構風格的約束,尤爲是要確保操做語義對於中間組件的可見性。 
在上面這兩個方面,Roy Fileidng曾經與SPDY協議設計者Mike Belshe發生過激烈爭論,詳情請看:Roy Fielding談Google SPDY協議

李建業:對 此規範關注很少,不知道會不會有對於流的支持,目前我所知道的只有chunk方式進行簡單的支持,可是真正的流須要區分數據通道和控制通道——哪怕是邏輯 上的區分,這樣就直接對REST風格產生了很大沖擊,考慮到流式服務在將來的發展潛力,我特別期待業界在這方面有所進展。
丁雪豐:HTTP 2.0 很大程度上是借鑑了Google的SPDY,就我而言,首先,但願這個規範能作到與HTTP 1.1的兼容,使用者若是隻認識1.1,那麼2.0能優雅 「降級」;其次,但願2.0能帶來更好的性能,SPDY在這方面仍是有所改進的,但願HTTP 2.0能再接再礪;最後,但願這個規範能在最終定稿時附帶 一個最佳實踐,正確引導人們合理地使用HTTP 2.0。

 

馬鈞:沒研究過,估計即便出來,1.1還有很長的生命週期,不會很快被取代
相關文章
相關標籤/搜索