1. 程式人生 > >奧塔線上:基於token認證的基礎知識詳解

奧塔線上:基於token認證的基礎知識詳解

一、    什麼是Token
Token原始的意思是“令牌”,是服務端生成的一個自定義字串,作為客戶端進行資料請求的一個標識。在區塊鏈興起後,Token被賦予“代幣”或“通證”的含義,意思是一種可流通的加密數字權益證明。

二、    Token的起源
早在計算機網路剛興起時,Token就應用在網路通訊中,它本身是一個網路術語。
在乙太網成為區域網的普遍協議之前,IBM 曾經推過一個區域網協議,叫做Token Ring Network,令牌環網。網路中的每一個節點輪流傳遞一個令牌,只有拿到令牌的節點才能通訊。這個令牌,其實就是一種權利,或者說權益證明。
網際網路時代,隨著Web API和物聯網的興起,基於Token的身份認證機制越來越被大家廣泛採用。通常情況下,在Web領域,Token主要用於身份認證和防止表單重複提交。在Web領域基於Token的身份驗證隨處可見,在大多數使用Web API的網際網路公司中,Token是多使用者下處理認證的最佳方式。
區塊鏈時代,以太坊及智慧合約概念的出現,賦予了Token更廣泛的含義。在區塊鏈提到的Token,其實大多是基於以太坊ERC-20標準的一種智慧合約產物。基於以太坊ERC-20這個標準,任何人都可以在以太坊上發行自定義的Token,這個Token可以代表任何權益和價值。
名詞解釋:

以太坊(英語:Ethereum)是一個開源的有智慧合約功能的公共區塊鏈平臺。截止2018年2月,以太幣是市值第二高的加密貨幣,僅次於比特幣。
智慧合約可以理解為在區塊鏈上可以自動執行的(由事件驅動的)、以程式碼形式編寫的合同(特殊的交易)。
三、    Token身份驗證原理
在瞭解Token身份驗證原理前,需要先理解傳統身份驗證方法,才能更透徹的理解相關概念。

3.1    傳統身份認證
由於HTTP協議是一種無狀態的協議,預設情況下服務端並不知道是誰訪問了自己,這就意味著客戶端的每次請求都需要進行驗證,從而辨別客戶端的身份。業界通常的解決方案是通過在服務端儲存登入資訊(如儲存到服務端Session裡)來辨別請求的。其業務流程如下:
1)    使用者輸入登陸憑據;
2)    伺服器驗證憑據是否正確,並建立會話(Session),然後把會話資料儲存在資料庫中(分散式會話);
3)    具有會話id的cookie被放置在使用者瀏覽器中;
4)    在後續請求中,伺服器會根據資料庫驗證會話id,如果驗證通過,則繼續處理;
5)    一旦使用者登出,服務端和客戶端同時銷燬該會話。

流程說明:

當客戶端進行登入請求的時候,如果沒有問題,在服務端生成一條記錄,在這個記錄裡可以說明登入的使用者是誰,然後把這條記錄的id傳送給客戶端,客戶端收到以後把這個id儲存在cookie裡,下次該使用者再次向服務端傳送請求的時候,可以帶上這個cookie,這樣服務端會驗證一下cookie裡的資訊,看能不能在服務端這裡找到對應的記錄,如果可以,說明使用者已經通過了身份驗證,就把使用者請求的資料返回給客戶端。

暴露的問題:
>Seesion:每次認證使用者發起請求時,伺服器需要去建立一個記錄來儲存資訊。當越來越多的使用者發請求時,記憶體的開銷也會不斷增加;
>可擴充套件性:在服務端的記憶體中使用Seesion儲存登入資訊,伴隨而來的是可擴充套件性問題,增加伺服器並不能解決Session,不過可以採用分散式Session方案,如使用資料庫儲存Session,每次請求被轉向到不同伺服器時,可以直接到資料庫中查詢對應Session;
>CORS(跨域資源共享):當我們需要讓資料跨多臺移動裝置上使用時,跨域資源的共享會是一個讓人頭疼的問題。在使用Ajax抓取另一個域的資源,就可能會出現禁止請求的情況;
>CSRF(跨站請求偽造):使用者在訪問敏感網站時,他們很容易受到跨站請求偽造的攻擊,並且能夠被利用其訪問其他的網站。

3.2    基於Token的身份認證

使用Token機制的身份驗證方法,在伺服器端不需要儲存使用者的登入記錄。大概的流程:
1)    客戶端使用使用者名稱和密碼請求登入;
2)    服務端收到請求,驗證使用者名稱和密碼;
3)    驗證成功後,服務端會生成一個Token,然後把這個Token傳送給客戶端;
4)    客戶端收到Token後把它儲存起來,可以放在cookie或者Local Storage(本地儲存)裡;
5)    客戶端每次向服務端傳送請求的時候都需要帶上服務端發給的Token;
6)    服務端收到請求,然後去驗證客戶端請求裡面帶的Token(實時計算),如果驗證成功,就向客戶端返回請求的資料。

當用戶第一次登入後,伺服器生成一個Token並將此Token返回給客戶端,以後客戶端只需帶上這個Token前來請求資料即可,無需再次帶上使用者名稱和密碼。

3.3    兩種認證方式對比

互動流程對比

序號   互動項   傳統身份認證   基於Token身份認證
1提交使用者名稱密碼需要   需要
2服務端驗證   需要   需要
3服務端狀態儲存   需要   不需要
4本地儲存   需要   需要
5服務端鑑權   需要   需要
從流程上來看,兩者沒有什麼本質區別,唯一的區別在於服務端是否儲存使用者狀態。

能力對比

序號   功能項   傳統身份認證   基於Token身份認證
1實時性   稍差
2時效限制   
3跨域   不支援   支援
4跨程式   不支援   支援
5移動應用   支援   支援
6多端聯動   不支援支援
7服務端狀態   有狀態無狀態
8擴充套件性   
9安全性   
說明:
實時性比較
在進行身份驗證時,傳統身份認證方式使用客戶端Cookie資料與服務端快取中Session資料進行對比,在單臺伺服器時速度非常快,多臺時需要其它機制(如資料庫)保障Session的同步,效率會有下降。而Token方式認證,由於無狀態導致需要在服務端進行計算對比,比有狀態單機認證方式稍微差些。

綜上所述,比起傳統的身份驗證方法,基於Token的認證擴充套件性更強,也更安全,非常適合用在Web應用或者移動應用上。
何為無狀態、有狀態?
伺服器所維護的與客戶互動活動的資訊稱為狀態資訊。不儲存任何狀態資訊的伺服器稱為無狀態伺服器(stateless server),反之則稱為有狀態伺服器(stateful server)。
3.4    基於Token認證的實現方式
Token驗證的方法挺多的,相關組織提供了一些標準方法。
1)    JWT
讀作:jot,表示:JSON Web Tokens。
JWT是一種安全標準,一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源。基本思路就是使用者提供使用者名稱和密碼給認證伺服器,伺服器驗證使用者提交資訊資訊的合法性;如果驗證成功,會產生並返回一個Token(令牌),使用者可以使用這個Token訪問伺服器上受保護的資源。
根據維基百科的定義,JWT是一種基於JSON的、用於在網路上宣告某種主張的令牌(Token)。JWT實際上就是一個字串,通常由三部分組成: 頭資訊(header), 訊息體(payload)和簽名(signature)。
JWT的實現流程與3.2中流程一致。

具體參見:
https://www.jianshu.com/p/168d34aab2e3

2)    OAuth
OAuth,表示:Open Authorization。
OAuth是一個關於授權的開放網路標準,它詳細描述了系統中不同角色、使用者、服務前端應用(比如API),以及客戶端(比如網站或移動App)之間怎麼實現相互認證。OAuth的作用就是讓"客戶端"安全可控地獲取"使用者"的授權,與"服務商提供商"進行互動,它允許使用者讓第三方應用訪問該使用者在某一web服務上儲存的私密的資源(如照片,視訊,聯絡人列表),而無需將使用者名稱和密碼提供給第三方應用。
OAuth的流程如下:
(A)使用者開啟客戶端以後,客戶端要求使用者給予授權;
(B)使用者同意給予客戶端授權;
(C)客戶端使用上一步獲得的授權,向認證伺服器申請令牌;
(D)認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌;
(E)客戶端使用令牌,向資源伺服器申請獲取資源;
(F)資源伺服器確認令牌無誤,同意向客戶端開放資源。

具體參見:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
四、    Token演算法定義
一個Token就是一些使用者資訊的集合,在Token中若包含足夠多的資訊,在後續請求中將會減少查詢資料庫的機率。
4.1    參考案例
首先,我們來看看微信公眾號的Token機制。
微信後臺在向公眾賬號伺服器傳送資料的時候,會額外帶上4個引數:timestamp、signature、nonce、echostr,如表3-1所示。其中timestamp是時間戳,nonce是一個隨機數,signature是對timestamp、nonce和Token進行SHA1加密後的字串。SHA1的加密過程是不可逆的,即不能通過timestamp、signature和nonce計算出Token是什麼。
在公眾賬號伺服器收到timestamp、signature和nonce之後,同樣對nonce、timestamp和Token使用SHA1加密演算法,得到自己的簽名,如果自己的簽名和請求中的signatrue是一樣的,那麼說明請求是來自微信後臺而不是惡意第三方。
注意,惡意的第三方有可能會截獲微信後臺發過來的timestamp、signature和nonce這三個引數,然後直接利用這個三個引數對公眾賬號伺服器發起請求。按照上面的邏輯可以知道,伺服器是無法判斷出這是個惡意的請求的。這種攻擊稱為replay攻擊。這種攻擊方式的防禦方法很簡單:加上對timestamp的校驗。在收到請求之後,將請求包中的timestamp與當前時間比較,如果誤差大於一定的值,就可認為這個請求是惡意的。這裡不能做相等的比較,因為資料在網路上傳輸需要時間,同時各個服務的本地時間也是有一些差異的。
微信伺服器通過檢驗signature對請求進行校驗(下面有校驗方式)。若此次GET的Token驗證可以通過,則表示該請求來自微信伺服器,這時會原樣返回echostr引數內容,表示接入生效,否則接入失敗。
詳見:https://blog.csdn.net/u014236541/article/details/49795247

4.2    內容組成元素

參照微信公眾號處理模式,在設計Token時,一般建議包含以下內容:

序號   鍵值   說明
1UID   使用者編號
2PID   服務提供者編號
3Key   金鑰
4SrcTag   來源平臺
5SrcIP   原始請求IP
6TimeStamp   時間戳,Token建立時間
7ExpireTime   過期時間
4.3    加密演算法
對包含的內容進行加密後輸出一個Token,一般常見的處理方式是先對內容進行Base64編碼,然後通過加密演算法(如使用JWT的HS256演算法,使用SHA1演算法)。
4.4    請求方式
客戶端在請求時,將Cookie中儲存的Token值通過Cookie、Http Header、Body裡面提交上來,具體如何傳遞Token需要看業務場景。比如網頁,可以直接通過Cookie;移動端應用可以在Http Header中,TV-STB上可在Http Body中通過Json傳遞。
客戶端在請求時,可參考微信公眾號的模式,請求Url中應包括時間戳、簽名、隨機碼,簽名應是通過時間戳+Token+隨機碼進行加密後獲得。當然,如果安全級別要求不高,可直接傳Token到後端進行驗證。
4.5    驗證方式
不同的加密演算法,以及是否採用無狀態模式,都決定了服務端的驗證方式。
如果採用有狀態模式,則在服務端可以通過Redis儲存Token及過期時間,在驗證時進行解密及解碼;如果無狀態模式,則需要客戶端提供足夠資訊由服務端實時計算後進行驗證。
五、    安全策略
從安全形度來說,Token並不能解決所有風險。由於Token是必須儲存到本地的,所以Token被洩露的機會很高。
在使用Token機制進行身份認證、操作鑑權的過程中,需要注意以下事項:
1)    Token時效性
伺服器應給Token設定一個有效期,每次客戶端請求的時候都驗證Token和有效期;
提供對某個Token主動過期的機制,如果Token被盜,有辦法及時將有效期內的Token失效。
2)    Token的儲存
Token可以存到資料庫中,但是有可能查詢Token的時間會過長導致Token丟失(其實Token丟失了再重新認證一個就好,但是別丟太頻繁,別讓使用者沒事兒就去認證)。
為了避免查詢時間過長,可以將Token放到記憶體中。這樣查詢速度絕對就不是問題了,也不用太擔心佔據記憶體,就算Token是一個32位的字串,應用的使用者量在百萬級或者千萬級,也是佔不了多少記憶體的。
3)    Token的加密
Token是很容易洩露的,如果不進行加密處理,很容易被惡意拷貝並用來登入。
在本地儲存的時候把Token進行對稱加密儲存,提交到服務端的時候再由服務端按規則解密。常見的方式為:將請求URL、時間戳、Token三者合併加鹽簽名,通過演算法進行加密處理。
另外在請求時,可要求提供簽名,簽名使用Token的前幾位以雜湊演算法壓縮成的一定長度的十六進位制字串。
4)    SSL協議請求
在網路層面上Token使用明文傳輸的話是非常危險的,所以一定要使用HTTPS協議。

最後需要特別指出的:風險控制是一個長期的鬥智鬥勇的過程,不能指望一個解決方案解決所有安全問題。