前後端分離開發,HTTP API 認證授權術

我們知道,HTTP 是無狀態的,所以,當我們需要獲得用戶是否在登錄的狀態時,我們需要檢查用戶的登錄狀態,一般來說,用戶的登錄成功後,服務器會發一個登錄憑證(又被叫作 Token),就像你去訪問某個公司,在前臺被認證過合法後,這個公司的前臺會給你的一個訪客卡一樣,之後,你在這個公司內去到哪都用這個訪客卡來開門,而不再校驗你是哪一個人。在計算機的世界裏,這個登錄憑證的相關數據會放在兩種地方,一個地方在用戶端,以 Cookie 的方式(一般不會放在瀏覽器的 Local Storage,因爲這很容易出現登錄憑證被 XSS 攻擊),另一個地方是放在服務器端,又叫 Session 的方式(SessonID 存於 Cookie)。

但是,這個世界還是比較複雜的,除了用戶訪問,還有用戶委託的第三方的應用,還有企業和企業間的調用,這裏,我想把業內常用的一些 API認證技術相對系統地總結歸納一下,這樣可以讓大家更爲全面的瞭解這些技術。注意,這是一篇長文!

本篇文章會覆蓋如下技術:

  • HTTP Basic
  • Digest Access
  • App Secret Key + HMAC
  • JWT – JSON Web Tokens
  • OAuth 1.0 – 3 legged & 2 legged
  • OAuth 2.0 – Authentication Code & Client Credential

HTTP Basic

HTTP Basic 是一個非常傳統的 API 認證技術,也是一個比較簡單的技術。這個技術也就是使用 username 和 password 來進行登錄。整個過程被定義在了 RFC 2617 中,也被描述在了 Wikipedia: Basic Access Authentication 詞條中,同時也可以參看 MDN HTTP Authentication

其技術原理如下:

 

  1. 把 username和 password 做成 username:password 的樣子(用冒號分隔)
  2. 進行 Base64 編碼。Base64("username:password") 得到一個字符串(如:把 haoel:coolshell 進行 base64 後可以得到 aGFvZW86Y29vbHNoZWxsCg )
  3. 把 aGFvZW86Y29vbHNoZWxsCg 放到 HTTP 頭中 Authorization 字段中,形成 Authorization: Basic aGFvZW86Y29vbHNoZWxsCg,然後發送到服務端。
  4. 服務端如果沒有在頭裏看到認證字段,則返回 401 錯,以及一個個 WWW-Authenticate: Basic Realm='HelloWorld' 之類的頭要求客戶端進行認證。之後如果沒有認證通過,則返回一個 401 錯。如果服務端認證通過,那麼會返回 200。

我們可以看到,使用 Base64 的目的無非就是爲了把一些特殊的字符給搞掉,這樣就可以放在 HTTP 協議裏傳輸了。而這種方式的問題最大的問題就是把用戶名和口令放在網絡上傳,所以,一般要配合 TLS/SSL 的安全加密方式來使用。我們可以看到 JIRA Cloud 的 API 認證支持HTTP Basic 這樣的方式。

但我們還是要知道,這種把用戶名和密碼同時放在公網上傳輸的方式有點不太好,因爲 Base64 不是加密協議,而是編碼協議,所以就算是有 HTTPS 作爲安全保護,給人的感覺還是不放心。

Digest Access

中文稱“HTTP 摘要認證”,最初被定義在了 RFC 2069 文檔中(後來被 RFC 2617 引入了一系列安全增強的選項;“保護質量”(qop)、隨機數計數器由客戶端增加、以及客戶生成的隨機數)。

其基本思路是,請求方把用戶名口令和域做一個 MD5 – MD5(username:realm:password) 然後傳給服務器,這樣就不會在網上傳用戶名和口令了,但是,因爲用戶名和口令基本不會變,所以,這個 MD5 的字符串也是比較固定的,因此,這個認證過程在其中加入了兩個事,一個是 nonce 另一個是 qop

  • 首先,調用方發起一個普通的 HTTP 請求。比如:GET /coolshell/admin/ HTTP/1.1 服務端自然不能認證能過,服務端返回 401 錯誤,並且在 HTTP 頭裏的 WWW-Authenticate 包含如下信息:
WWW-Authenticate: Digest realm="[email protected]", 
                       qop="auth,auth-int", 
                       nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", 
                       opaque="5ccc069c403ebaf9f0171e9517f40e41" 
  • 其中的 nonce 爲服務器端生成的隨機數,然後,客戶端做 HASH1=MD5(MD5(username:realm:password):nonce:cnonce) ,其中的 cnonce 爲客戶端生成的隨機數,這樣就可以使得整個 MD5 的結果是不一樣的。
  • 如果 qop 中包含了 auth ,那麼還得做 HASH2=MD5(method:digestURI) 其中的 method 就是HTTP的請求方法(GET/POST…),digestURI 是請求的URL。
  • 如果 qop 中包含了 auth-init ,那麼,得做 HASH2=MD5(method:digestURI:MD5(entityBody)) 其中的 entityBody 就是HTTP請求的整個數據體。
  • 然後,得到 response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2) 如果沒有 qop 則 response = MD5(HA1:nonce:HA2)
  • 最後,我們的客戶端對服務端發起如下請求—— 注意HTTP頭的 Authorization: Digest ...
GET /dir/index.html HTTP/1.0 
Host: localhost 
Authorization: Digest username="Mufasa", 
                     realm="[email protected]", 
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", 
                     uri="%2Fcoolshell%2Fadmin", 
                     qop=auth, 
                     nc=00000001, 
                     cnonce="0a4f113b", 
                     response="6629fae49393a05397450978507c4ef1", 
                     opaque="5ccc069c403ebaf9f0171e9517f40e41" 

維基百科上的 Wikipedia: Digest access authentication 詞條非常詳細地描述了這個細節。

摘要認證這個方式會比之前的方式要好一些,因爲沒有在網上傳遞用戶的密碼,而只是把密碼的 MD5 傳送過去,相對會比較安全,而且,其並不需要是否 TLS/SSL 的安全鏈接。但是,別看這個算法這麼複雜,最後你可以發現,整個過程其實關鍵是用戶的 password,這個 password 如果不夠得雜,其實是可以被暴力破解的,而且,整個過程是非常容易受到中間人攻擊——比如一箇中間人告訴客戶端需要的 Basic 的認證方式 或是 老舊簽名認證方式(RFC2069)。

App Secret Key + HMAC

先說 HMAC 技術,這個東西來自於 MAC – Message Authentication Code,是一種用於給消息簽名的技術,也就是說,我們怕消息在傳遞的過程中被人修改,所以,我們需要用對消息進行一個 MAC 算法,得到一個摘要字串,然後,接收方得到消息後,進行同樣的計算,然後比較這個 MAC 字符串,如果一致,則表明沒有被修改過(整個過程參看下圖)。而 HMAC – Hash-based Authenticsation Code,指的是利用 Hash 技術完成這一工作,比如:SHA-256算法。

我們再來說 App ID,這個東西跟驗證沒有關係,只是用來區分,是誰來調用 API 的,就像我們每個人的身份證一樣,只是用來標註不同的人,不是用來做身份認證的。與前面的不同之處是,這裏,我們需要用 App ID 來映射一個用於加密的密鑰,這樣一來,我們就可以在服務器端進行相關的管理,我們可以生成若干個密鑰對(AppID, AppSecret),並可以有更細粒度的操作權限管理。

 

  1. 把 AppID 和 HMAC 用於 API 認證,目前來說,玩得最好最專業的應該是 AWS 了,我們可以通過 S3 的 API 請求籤名文檔看到 AWS 是怎麼玩的。整個過程還是非常複雜的,可以通過下面的圖片流程看個大概。基本上來說,分成如下幾個步驟:
  2. 把 HTTP 的請求(方法、URI、查詢字串、頭、簽名頭,body)打個包叫 CanonicalRequest,作個 SHA-256 的簽名,然後再做一個 base16 的編碼
  3. 把上面的這個簽名和簽名算法 AWS4-HMAC-SHA256、時間戳、Scop,再打一個包,叫 StringToSign。
  4. 準備簽名,用 AWSSecretAccessKey 來對日期籤一個 DataKey,再用 DataKey 對要操作的 Region 籤一個 DataRegionKey ,再對相關的服務籤一個 DataRegionServiceKey ,最後得到 SigningKey.
  5. 用第三步的 SigningKey 來對第二步的 StringToSign 簽名。

最後,發出 HTTP Request 時,在 HTTP 頭的 Authorization 字段中放入如下的信息:

Authorization: AWS4-HMAC-SHA256 
               Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, 
               SignedHeaders=content-type;host;x-amz-date, 
               Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7 

其中的 AKIDEXAMPLE 是 AWS Access Key ID, 也就是所謂的 AppID,服務器端會根據這個 AppID 來查相關的 Secret Access Key,然後再驗證簽名。如果,你對這個過程有點沒看懂的話,你可以讀一讀這篇文章——《Amazon S3 Rest API with curl》這篇文章裏有好些代碼,代碼應該是最有細節也是最準確的了。

這種認證的方式好處在於,AppID 和 AppSecretKey,是由服務器的系統開出的,所以,是可以被管理的,AWS 的 IAM 就是相關的管理,其管理了用戶、權限和其對應的 AppID 和AppSecretKey。但是不好的地方在於,這個東西沒有標準 ,所以,各家的實現很不一致。比如:Acquia 的 HMAC,微信的簽名算法 (這裏,我們需要說明一下,微信的 API 沒有遵循HTTP 協議的標準,把認證信息放在 HTTP 頭的 Authorization 裏,而是放在 body 裏)

JWT – JSON Web Tokens

JWT 是一個比較標準的認證解決方案,這個技術在 Java 圈裏應該用的是非常普遍的。JWT 簽名也是一種 MAC(Message Authentication Code)的方法。JWT 的簽名流程一般是下面這個樣子:

用戶使用用戶名和口令到認證服務器上請求認證。

認證服務器驗證用戶名和口令後,以服務器端生成 JWT Token,這個 token 的生成過程如下:

  • 認證服務器還會生成一個 Secret Key(密鑰)
  • 對JWT Header和 JWT Payload 分別求 Base64。在 Payload 可能包括了用戶的抽象 ID 和的過期時間。
  • 用密鑰對 JWT 簽名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));

然後把 base64(header).base64(payload).signature 作爲 JWT token 返回客戶端。

客戶端使用 JWT Token 嚮應用服務器發送相關的請求。這個 JWT Token 就像一個臨時用戶權證一樣。

當應用服務器收到請求後:

  1. 應用服務會檢查 JWT Token,確認簽名是正確的。
  2. 然而,因爲只有認證服務器有這個用戶的 Secret Key(密鑰),所以,應用服務器得把 JWT Token 傳給認證服務器。
  3. 認證服務器通過 JWT Payload 解出用戶的抽象 ID,然後通過抽象 ID 查到登錄時生成的 Secret Key,然後再來檢查一下簽名。
  4. 認證服務器檢查通過後,應用服務就可以認爲這是合法請求了。

我們可以看以,上面的這個過程,是在認證服務器上爲用戶動態生成 Secret Key 的,應用服務在驗籤的時候,需要到認證服務器上去籤,這個過程增加了一些網絡調用,所以,JWT 除了支持 HMAC-SHA256 的算法外,還支持 RSA 的非對稱加密的算法。

使用 RSA 非對稱算法,在認證服務器這邊放一個私鑰,在應用服務器那邊放一個公鑰,認證服務器使用私鑰加密,應用服務器使用公鑰解密,這樣一來,就不需要應用服務器向認證服務器請求了,但是,RSA 是一個很慢的算法,所以,雖然你省了網絡調用,但是卻費了 CPU,尤其是Header 和 Payload 比較長的時候。所以,一種比較好的玩法是,如果我們把 header 和 payload 簡單地做 SHA256,這會很快,然後,我們用 RSA 加密這個 SHA256 出來的字符串,這樣一來,RSA 算法就比較快了,而我們也做到了使用 RSA 簽名的目的。

最後,我們只需要使用一個機制在認證服務器和應用服務器之間定期地換一下公鑰私鑰對就好了。

這裏強烈建議全文閱讀 Anglar 大學的 《JSW:The Complete Guide to JSON Web Tokens》

OAuth 1.0

OAuth 也是一個 API 認證的協議,這個協議最初在 2006 年由 Twitter 的工程師在開發 OpenID 實現的時候和社交書籤網站 Ma.gnolia 時發現,沒有一種好的委託授權協議,後來在 2007 年成立了一個 OAuth 小組,知道這個消息後,Google 員工也加入進來,並完善有善了這個協議,在 2007 年底發佈草案,過一年後,在 2008 年將 OAuth 放進了 IETF 作進一步的標準化工作,最後在 2010 年 4 月,正式發佈 OAuth 1.0,即:RFC 5849 (這個 RFC 比起 TCP 的那些來說讀起來還是很輕鬆的),不過,如果你想了解其前身的草案,可以讀一下 OAuth Core 1.0 Revision A ,我在下面做個大概的描述。

根據RFC 5849,可以看到 OAuth 的出現,目的是爲了,用戶爲了想使用一個第三方的網絡打印服務來打印他在某網站上的照片,但是,用戶不想把自己的用戶名和口令交給那個第三方的網絡打印服務,但又想讓那個第三方的網絡打印服務來訪問自己的照片,爲了解決這個授權的問題, OAuth 這個協議就出來了。

這個協議有三個角色:

  • User(照片所有者-用戶)
  • Consumer(第三方照片打印服務)
  • Service Provider(照片存儲服務)

這個協義有三個階段:

  • Consumer 獲取 Request Token
  • Service Provider 認證用戶並授權 Consumer
  • Consumer 獲取 Access Token 調用 API 訪問用戶的照片

整個授權過程是這樣的:

 

  1. Consumer(第三方照片打印服務)需要先上 Service Provider 獲得開發的 Consumer Key 和 Consumer Secret
  2. 當 User 訪問 Consumer 時,Consumer 向 Service Provide 發起請求請求Request Token (需要對HTTP請求籤名)
  3. Service Provide 驗明 Consumer 是註冊過的第三方服務商後,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
  4. Consumer 收到 Request Token 後,使用 HTTP GET 請求把 User 切到 Service Provide 的認證頁上(其中帶上Request Token),讓用戶輸入他的用戶和口令。
  5. Service Provider 認證 User 成功後,跳回 Consumer,並返回 Request Token (oauth_token)和 Verification Code(oauth_verifier)
  6. 接下來就是簽名請求,用 Request Token 和 Verification Code 換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  7. 最後使用 Access Token 訪問用戶授權訪問的資源。

下圖附上一個 Yahoo! 的流程圖可以看到整個過程的相關細節。

因爲上面這個流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三腳流程。OAuth 1.0 也有不需要用戶參與的,只有 Consumer 和 Service Provider 的, 也就是 2-legged flow 兩腳流程,其中省掉了用戶認證的事。整個過程如下所示:

  1. Consumer(第三方照片打印服務)需要先上 Service Provider 獲得開發的 Consumer Key 和 Consumer Secret
  2. Consumer 向 Service Provide 發起請求請求 Request Token (需要對 HTTP 請求籤名)
  3. Service Provide 驗明 Consumer 是註冊過的第三方服務商後,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
  4. Consumer 收到 Request Token 後,直接換取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  5. 最後使用 Access Token 訪問用戶授權訪問的資源。

最後,再來說一說 OAuth 中的簽名。

  • 我們可以看到,有兩個密鑰,一個是 Consumer 註冊 Service Provider 時由 Provider 頒發的 Consumer Secret,另一個是 Token Secret。
  • 簽名密鑰就是由這兩具密鑰拼接而成的,其中用 & 作連接符。假設 Consumer Secret 爲 j49sk3j29djd 而 Token Secret 爲 dh893hdasih9 那個,簽名密鑰爲:j49sk3j29djd&dh893hdasih9
  • 在請求 Request/Access Token 的時候需要對整個 HTTP 請求進行簽名(使用 HMAC-SHA1 和 HMAC-RSA1 簽名算法),請求頭中需要包括一些 OAuth 需要的字段,如:
    • Consumer Key :也就是所謂的 AppID
    • Token:Request Token 或 Access Token
    • Signature Method :簽名算法比如:HMAC-SHA1
    • Timestamp:過期時間
    • Nonce:隨機字符串
    • Call Back:回調 URL

下圖是整個簽名的示意圖:

圖片還是比較直觀的,我就不多解釋了。

OAuth 2.0

在前面,我們可以看到,從 Digest Access, 到 AppID+HMAC,再到 JWT,再到 OAuth 1.0,這些個 API 認證都是要向 Client發一個密鑰(或是用密碼)然後用 HASH 或是 RSA 來籤 HTTP 的請求,這其中有個主要的原因是,以前的 HTTP 是明文傳輸,所以,在傳輸過程中很容易被篡改,於是才搞出來一套的安全簽名機制,所以,這些個認證的玩法是可以在 HTTP 明文協議下玩的。

這種使用簽名方式大家可以看到是比較複雜的,所以,對於開發者來說,也是很不友好的,在組織簽名的那些 HTTP 報文的時候,各種,URLEncode 和 Base64,還要對 Query 的參數進行排序,然後有的方法還要層層簽名,非常容易出錯,另外,這種認證的安全粒度比較粗,授權也比較單一,對於有終端用戶參與的移動端來說也有點不夠。所以,在 2012 年的時候,OAuth 2.0 的 RFC 6749 正式放出。

 

OAuth 2.0 依賴於 TLS/SSL 的鏈路加密技術(HTTPS),完全放棄了簽名的方式,認證服務器再也不返回什麼 token secret 的密鑰了,所以,OAuth 2.0 是完全不同於 1.0 的,也是不兼容的。目前,Facebook 的 Graph API 只支持 OAuth 2.0協議,Google 和 Microsoft Azure 也支持Auth 2.0,國內的微信和支付寶也支持使用 OAuth 2.0。

下面,我們來重點看一下 OAuth 2.0 的兩個主要的 Flow:

  • 一個是 Authorization Code Flow, 這個是 3 legged 的
  • 一個是 Client Credential Flow,這個是 2 legged 的。

Authorization Code Flow

Authorization Code 是最常使用的 OAuth 2.0 的授權許可類型,它適用於用戶給第三方應用授權訪問自己信息的場景。這個 Flow 也是 OAuth 2.0 四個 Flow 中我個人覺得最完整的一個 Flow,其流程圖如下所示。

下面是對這個流程的一個細節上的解釋:

1)當用戶(Resource Owner)訪問第三方應用(Client)的時候,第三方應用會把用戶帶到認證服務器(Authorization Server)上去,主要請求的是 /authorize API,其中的請求方式如下所示。

https://login.authorization-server.com/authorize? 
        client_id=6731de76-14a6-49ae-97bc-6eba6914391e 
        &response_type=code 
        &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F 
        &scope=read 
        &state=xcoiv98CoolShell3kch 

其中:

  • client_id 爲第三方應用的 App ID
  • response_type=code 爲告訴認證服務器,我要走 Authorization Code Flow。
  • redirect_uri 意思是我跳轉回第三方應用的 URL
  • scope 意是相關的權限
  • state 是一個隨機的字符串,主要用於防 CSRF 攻擊。

2)當 Authorization Server 收到這個 URL 請求後,其會通過 client_id 來檢查 redirect_uri 和 scope 是否合法,如果合法,則彈出一個頁面,讓用戶授權(如果用戶沒有登錄,則先讓用戶登錄,登錄完成後,出現授權訪問頁面)。

3)當用戶授權同意訪問以後,Authorization Server 會跳轉回 Client ,並以其中加入一個 Authorization Code。如下所示:

https://example-client.com/callback? 
        code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG 
        &state=xcoiv98CoolShell3kch 

我們可以看到,

  • 請流動的鏈接是第 1)步中的 redirect_uri
  • 其中的 state 的值也和第 1)步的 state一樣。

4)接下來,Client 就可以使用 Authorization Code 獲得 Access Token。其需要向 Authorization Server 發出如下請求。

POST /oauth/token HTTP/1.1 
Host: authorization-server.com 
  
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG 
&grant_type=code 
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F 
&client_id=6731de76-14a6-49ae-97bc-6eba6914391e 
&client_secret=JqQX2PNo9bpM0uEihUPzyrh 

5)如果沒什麼問題,Authorization 會返回如下信息。

{ 
  "access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI", 
  "refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU", 
  "token_type": "bearer", 
  "expires": 3600, 
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..." 
} 

其中,

  • access_token 就是訪問請求令牌了
  • refresh_token 用於刷新 access_token
  • id_token 是 JWT 的 token,其中一般會包含用戶的 OpenID

6)接下來就是用 Access Token 請求用戶的資源了。

GET /v1/user/pictures 
Host: https://example.resource.com 
 
Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI 

Client Credential Flow

Client Credential 是一個簡化版的 API 認證,主要是用於認證服務器到服務器的調用,也就是沒有用戶參與的的認證流程。下面是相關的流程圖。

這個過程非常簡單,本質上就是 Client 用自己的 client_id 和 client_secret 向Authorization Server 要一個 Access Token,然後使用 Access Token 訪問相關的資源。

請求示例

POST /token HTTP/1.1 
Host: server.example.com 
Content-Type: application/x-www-form-urlencoded 
 
grant_type=client_credentials 
&client_id=czZCaGRSa3F0Mzpn 
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw 

返回示例

{ 
  "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", 
  "token_type":"bearer", 
  "expires_in":3600, 
  "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", 
  "scope":"create" 
} 

這裏,容我多扯一句,微信公從平臺的開發文檔中,使用了 OAuth 2.0 的 Client Credentials 的方式(參看文檔“微信公衆號獲取 access token”),我截了個圖如下所謂。我們可以看到,微信公衆號使用的是 GET 方式的請求,把 AppID 和 AppSecret 放在了 URL中,雖然這也符合 OAuth 2.0,但是並不好,因爲大多數網關代理會把整個 URI 請求記到日誌中。我們只要腦補一下騰訊的網關的 Access Log,裏面的日誌一定會有很多的各個用戶的AppID 和 AppSecret……

小結

講了這麼多,我們來小結一下(下面的小結可能會有點散)

兩個概念和三個術語

  • 區分兩個概念:Authentication(認證) 和 Authorization (授權),前者是證明請求者是身份,就像身份證一樣,後者是爲了獲得權限。身份是區別於別人的證明,而權限是證明自己的特權。Authentication 爲了證明操作的這個人就是他本人,需要提供密碼、短信驗證碼,甚至人臉識別。Authorization 則是不需要在所有的請求都需要驗人,是在經過 Authorization 後得到一個 Token,這就是 Authorization。就像護照和簽證一樣。
  • 區分三個概念:編碼 Base64Encode、簽名 HMAC、加密 RSA。編碼是爲了更的傳輸,等同於明文,簽名是爲了信息不能被篡改,加密是爲了不讓別人看到是什麼信息。

明白一些初衷

  • 使用複雜地 HMAC 哈希簽名方式主要是應對當年沒有 TLS/SSL 加密鏈路的情況。
  • JWT 把 uid 放在 Token 中目的是爲了去掉狀態,但不能讓用戶修改,所以需要簽名。
  • OAuth 1.0 區分了兩個事,一個是第三方的 Client,一個是真正的用戶,其先拿 Request Token,再換 Access Token 的方法主要是爲了把第三方應用和用戶區分開來。
  • 用戶的 Password 是用戶自己設置的,複雜度不可控,服務端頒發的 Serect 會很複雜,但主要目的是爲了容易管理,可以隨時註銷掉。
  • OAuth 協議有比所有認證協議有更爲靈活完善的配置,如果使用 AppID/AppSecret 簽名的方式,又需要做到可以有不同的權限和可以隨時註銷,那麼你得開發一個像 AWS 的 IAM 這樣的賬號和密鑰對管理的系統。

相關的注意事項

  • 無論是哪種方式,我們都應該遵循 HTTP 的規範,把認證信息放在 Authorization HTTP 頭中。
  • 不要使用 GET 的方式在 URL 中放入 secret 之類的東西,因爲很多 proxy 或 gateway 的軟件會把整個 URL 記在 Access Log 文件中。
  • 密鑰 Secret 相當於 Password,但他是用來加密的,最好不要在網絡上傳輸,如果要傳輸,最好使用 TLS/SSL 的安全鏈路。
  • HMAC 中無論是 MD5 還是 SHA1/SHA2,其計算都是非常快的,RSA 的非對稱加密是比較耗 CPU 的,尤其是要加密的字符串很長的時候。
  • 最好不要在程序中 hard code 你的 Secret,因爲在 github 上有很多黑客的軟件在監視各種 Secret,千萬小心!這類的東西應該放在你的配置系統或是部署系統中,在程序啓動時設置在配置文件或是環境變量中。
  • 使用 AppID/AppSecret,還是使用 OAuth1.0a,還是 OAuth2.0,還是使用 JWT,我個人建議使用 TLS/SSL 下的 OAuth 2.0。
  • 密鑰是需要被管理的,管理就是可以新增可以撤銷,可以設置賬戶和相關的權限。最好密鑰是可以被自動更換的。
  • 認證授權服務器(Authorization Server)和應用服務器(App Server)最好分開。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章