閱讀907 返回首頁    go 阿裏雲 go 技術社區[雲棲]


OAuth工作原理隨想——讓你的係統提供的服務更加安全

最近這段時間,一直都在和web服務打交道。自己項目組的係統需要別的項目組提供服務接口;別的平台(手機)平台又需要我們這邊給它們提供接口。實現、調用、接口文檔都有所涉及。從中我發現一個非常重要的問題——安全,這是一個被嚴重忽略的問題。

我認為在網絡這個充滿敵意的大環境下,應用和服務的安全性,是一個不得不重視的問題。去年年底的CSDN賬號泄露以及口令明文的事件,至少給了企業兩個最基本的警示:(1)不要等到出現問題之後,才知道要去挽救,在這個浮躁的社會氛圍下,出現哪怕不是什麼大問題,都會被群起而攻之;(2)服務的提供方應該認識到安全的重要性。本文參考OAuth協議的工作原理,給出了我個人覺得更為安全的服務認證和授權方式。一家之談,歡迎各位提出寶貴意見。

幾個項目組對於web服務的處理方式:

(1) 僅僅是一個簡單的明文key,用來標識屬於哪個平台;

(2) 服務提供方和調用方協商一種驗證碼的生成規則,服務的調用方攜帶用戶id以及生成的驗證碼。在服務提供方一端有個驗證的方式,如果按照同樣的規則生成出相同的驗證碼,則認為調用方是合法調用者

簡單的談談這兩種方式吧,其實第一種沒什麼好談的。它隻是用了一個標識符,標識服務的調用方來自哪個平台,然後在它方法中表明應該走入哪個分支,這顯而易見。還有一個我覺得沒什麼好談的就是,它簡直就是個“肉雞”,甚至連“肉雞”都算不上。隻要你知道URL,想幹嘛幹嘛去吧;第二種方式,不得不說至少比第一種方式高級得多。它至少仿照了類似數字簽名的做法。但不得不說,還是有些簡單並直接了。並且,要知道隻要基於了同一種生成驗證碼的規則(注意這裏並不是hash散列算法),那麼它的靈活性和隨機性就會降低。有一種比較簡單而有效的攻擊手段——DOS(拒絕服務),上麵兩種方式都無法逃過這樣的攻擊。原因是:

(1) URL是定死的,或者很長一段時間才會改變為另一種形式的,但在服務端並沒有關於對來自同一個IP,在一段時間內訪問次數的限製

(2) 沒有對調用方進行認證

對於采用HTTP GET形式的Web Service調用都會有這些安全問題。後台引用的調用方式也難以幸免於難,因為它們本質上都是一樣的。其實,一方需要另一方提供服務,這種場景在互聯網中早已司空見慣。新浪、騰訊、人人、網易等開放平台都是服務的提供者,它們是如何來保證服務的安全性以達到保證平台用戶資源的安全?沒錯,它們幾乎都是使用——OAuth這種認證和授權協議。

OAuth協議簡介

具體的文字解釋,參見wiki——OAuth

應用場景:



請求過程示意圖:



項目目前對於web服務【web service/web page】的調用場景:




OAuth照搬?

OAuth的認證和授權過程中,通常都有一步需要用戶授權的過程,也就是需要將用戶引導到第三方服務提供者的授權頁麵(通常需要填寫用戶名和密碼),並引導用戶完成授權。在係統中,並不適合引入這樣的交互模式。因為我們現在的係統從第三方提取數據對用戶來講是透明的,用戶登錄我們的係統,並不知道,我們的數據是來自第三方提供的。所以這極其影響用戶體驗。並且,參照資源的重要級別,並不是所有服務都需要享受這麼機密的待遇(從Oath授權可以看到,需要多次的“握手”,也就是需要來回HTTP請求好幾次),有時資源重要性是需要考慮的一個方麵,而有時保證服務正常提供、提供服務的服務器正常運轉則更為側重。

如何兼顧安全、性能?

下圖展示了本人對於兼顧安全以及性能的設想:


對於步驟說明:

第一次交互稱之為:握手(也就是認證和授權的過程),由於這些服務本質上隻對已知係統的特定用戶開放,而非互聯網應用的開放級別。所以這裏省去了第一步驗證客戶端身份,獲取Request Token的過程(或者稱之為合二為一)。

步驟1:請求方采用HTTPS協議,封送能夠認證調用方合法身份。這裏封送什麼內容呢?可以是服務提供方分配的用戶名或密碼對,可以是類似於上麵的驗證碼,還可以是提供方公開的密鑰。以上這些針對公共資源服務就足夠了,對於類似微博那些設計到私有用戶的資源,需要能夠提供服務方用戶檢索特定資源的標識(很明顯這些標識在服務調用方必須已知,比如userId等XXXID,如果不得不需要,可以在用戶注冊服務調用方係統的時候自行填寫)。

這裏你可能有一個疑問,公共資源就算了,受限資源為什麼不需要用戶在服務提供方平台的密碼?這裏還是涉及到信任級別的問題,我認為這確實是一個需要權衡的問題。或者說需要視特定應用場景以及資源的保護級別而定。上麵提供的那些認證信息(包括XXXID,服務提供方會在特定的數據表中檢索,以再次驗證請求的合法性),足以表明服務的調用方確實是可信的,對於保護級別不是特別高(特別是應對公司內部,係統與係統之間的交互場景),最重要的是檢索數據的服務而言,就可以允許調用方獲取它想提取的信息了。當然如果,確實需要得到資源擁有者的授權,那麼把用戶定向到服務提供者的授權頁麵,也未嚐不可。

步驟2:返回服務調用方Access Token.這是關鍵的一步。與開始我們談到的我們既有的兩種“驗證方式”根本的不同點在於——服務提供者能夠控製局麵。這裏可以實現各種不同的安全機製,限製訪問時間,限製訪問次數,限製IP在某一時間段內的訪問次數等等。你可以在服務提供程序中,對於前來握手的服務調用方的信息進行維護,如:

Application[“172.40.38.1”]=”asdfj2093423uwqesdjfdfepclkjd”;

采用訪問者的ip作為key ,服務端生成的一串accsee token作為value,在服務提供端進行維護。當然,你可以定義一個結構:

class visitorInfo{
	private int totalCount;		//允許的總訪問次數
	private int currentCount;		//當前已經訪問次數
	private DateTime startTime;		//會話的開始時間
	private DateTime endTime;		//會話的結束時間
	Private string accessToken;	//訪問令牌
	Private string reflushToken;	//刷新token(可選的)
}

以進行更合理的控製並作日誌記錄,至於怎麼維護這個結構,在內存中還是持久化到數據庫,這個不是這篇的話題。

步驟2的輸出就是訪問令牌當然你也可以加上一個刷新令牌(這個是可選的,主要是為了在一次會話過期後,調用方可以拿著刷新令牌,直接換取一個新的訪問令牌,而不需要重新認證,當然你也可以選擇讓調用方重新認證。

第二次交互:服務的請求和應答,這裏就沒有什麼特別的了。

步驟3:調用方拿著服務提供方授予的access token或者用戶檢索受限資源的XXXID,就可以請求服務了(隻是普通的http請求)。這裏,局麵就可以完全被服務調用方hold住,它可以對IP和Access Token進行驗證,要是有必要,也可以對會話是否過期,或者一段時間內的訪問次數進行驗證,而無需擔心惡意中間人的重放或DOS攻擊。因為服務方維護了認證過的調用方的access token以及IP。

步驟4:應答請求。

總結

這裏參考了OAuth協議的工作原理,並加以精簡以適應某些係統對係統提供服務的場景。再次聲明,這裏隻是在安全和性能之間找了一個平衡點,使得你的服務應用更為堅固。如果你想要更為安全的做法,可以完全參照OAuth的設計,強製要求用戶進行授權(上麵已經解釋了應該如何處理)。

無論如何,對於這種共享數據之類的服務,協商和人為交互的頻度還是比較大的,並且效率不是太高。有沒有從根本上解決這種問題的辦法?有,那就是不提供這些服務。那應該怎麼辦?下一篇推崇——淺談企業業務數據的整合。敬請期待!




原文發布時間為:2012-03-04


本文作者:vinoYang


本文來自雲棲社區合作夥伴CSDN博客,了解相關信息可以關注CSDN博客。

最後更新:2017-11-24 00:34:26

  上一篇:go  純幹貨!阿裏安全謝君:如何黑掉無人機
  下一篇:go  關於先知創新大會的那些"為什麼"?