開放平臺鑒權以及OAuth2.0介紹
OAuth 2.0 協議
- OAuth是一個開發標準,允許用戶授權第三方網站或應用訪問他們存儲在另外的服務提供者上的信息,而不需要將用戶名和密碼提供給第三方網站或分享他們數據的內容。
- OAuth 2.0不兼容1.0。
協議的參與者
- RO (resource owner): 資源所有者,對資源具有授權能力的人。
- RS (resource server): 資源服務器,它存儲資源,並處理對資源的訪問請求。
- Client: 第三方應用,它獲得RO的授權後便可以去訪問RO的資源。
- AS (authorization server): 授權服務器,它認證RO的身份,為RO提供授權審批流程,並最終頒發授權令牌(Access Token)。
授權方式
在開放授權中,第三方應用(Client)可能是一個Web站點,也可能是在瀏覽器中運行的一段JavaScript代碼,還可能是安裝在本地的一個應用程序。這些第三方應用都有各自的安全特性。對於Web站點來說,它與RO瀏覽器是分離的,它可以自己保存協議中的敏感數據,這些密鑰可以不暴露給RO;對於JavaScript代碼和本地安全的應用程序來說,它本來就運行在RO的瀏覽器中,RO是可以訪問到Client在協議中的敏感數據。
OAuth2.0為了支持這些不同類型的第三方應用,提出了下面四種授權類型:
- 授權碼 (Authorization Code Grant),適用於有server端的應用授權。
- 隱式授權 (Implicit Grant),適用於通過客戶端訪問的應用授權。
- 資源所有者密碼憑證許可 (Resource Owner Password Credentials Grant),OAuth簡化版,常用於移動應用認證,稱為xAuth。
- 受保護資源的客戶端授權 (Client Credentials Grant)。
流程 | Response Type(第一次請求) | Grant Type(第二次請求) | 可帶Refresh Token | 說明 |
---|---|---|---|---|
授權碼 | code | authorization_code | 是 | 常規流程 |
Implicit | token | - | 否 | 適用於純JS程序 |
用戶認證 | - | password | 是 | 客戶端高度可信,且授權碼流程不方便實施 |
客戶端 | - | client_credentials | 否 | 客戶端高度可信,擁有被操作資源(自用型),或操作非敏感資源 |
Authorization Code 授權
1. 授權場景
Authorization code 授權適用於PC,無線客戶端等需要和第三方server進行交互的應用場景。使用Authorization code授權,第三方能夠集中處理用戶的授權請求和授權結果,適用於有server端的應用。
2. 授權流程
Authorization code授權模式分為兩步,首先獲取authorization code,然後用code獲取acces token。
示意圖:
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(B)-- User authenticates --->| Server | | | | | | -+----(C)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (A) (C) | | | | | | ^ v | | +---------+ | | | |>---(D)-- Authorization Code ---------‘ | | Client | & Redirection URI | | | | | |<---(E)----- Access Token -------------------‘ +---------+ (w/ Optional Refresh Token)
交互圖:
+--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+
3. 過程詳解
1、獲取Authorization Code
請求參數
- client_id 必須 分配給應用的appid
- redirect_uri 必須 授權回調地址,必須和應用註冊的地址一致
- response_type 必須 授權類型,此值固定為“code”
- state 必須 client端的狀態值。用於第三方應用防止CSRF攻擊,成功授權後回調時會原樣帶回。請務必嚴格按照流程檢查用戶與state參數狀態的綁定。
- scope 可選 授予權限範圍
- 其他參數
如果用戶成功授權,則會跳轉到指定的回調地址,並在redirect_uri地址後帶上Authorization Code和原始的state值
2、通過Authorization Code獲取Access Token
請求參數
- client_id 必須 分配給應用的appid
- grant_type 必須 授權類型,此值為:authorization_code
- client_secret 必須 分配給應用的secret
- state 必須 client端的狀態值。用於第三方應用防止CSRF攻擊,成功授權後回調時會原樣帶回。請務必嚴格按照流程檢查用戶與state參數狀態的綁定。
- redirect_uri 必須 與上面一步中傳入的redirect_uri保持一致
- code 必須 上一步返回的Authorization Code值(必須設定此code的有效時間)
返回參數
- access_token 必須 授權令牌
- expires_in 必須 該access_token的有效期
- refresh_token 可選 在授權自動續期步驟中,獲取新的Access_Token時需要提供的參數
- 其他參數 可選
3、[可選] 權限自動續期,獲取access_token
Access_token一般需要根據應用特性設定有效期,過期後需要用戶重新授權或采用自動續期的方式。
請求參數
- grant_type 必須 授權類型,在本步驟中,此值為“refresh_token”
- client_id 必須 分配給應用的appid
- refresh_token 必須 第二步返回的refresh_token
- client_secret 必須 分配給應用的secret
如果授權成功,則會返回和步驟二同樣的結果。
Implicit 授權
1. 授權場景
Implicit授權一般適用於沒有server端的客戶端應用,由客戶端發起授權請求,保存和處理access_token,但有些應用(如web應用等)為了提高用戶體驗,簡化授權過程,也會常采用Implicit授權方式(註意,這種授權方式沒有返回refresh_token。)
2. 授權流程
示意圖:
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+ Note: The lines illustrating steps (A) and (B) are broken into two parts as they pass through the user-agent.
交互圖:
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
交互圖:
3. 過程詳解
請求參數
- response_type 必須 授權類型,在本步驟中,此值為“token”
- client_id 必須 分配給應用的appid
- redirect_uri 必須 授權回調地址,必須和應用註冊的地址一致
- scope 可選 授予權限範圍
- state 必須 client端的狀態值。用於第三方應用防止CSRF攻擊,成功授權後回調時會原樣帶回。請務必嚴格按照流程檢查用戶與state參數狀態的綁定。
- 其他參數 可選
如果成功授權,則會跳轉到redirect_uri指定的回調地址,並帶上access_token、expires_in、state以及與應用相關的參數。註意,這種授權方式沒有返回refresh_token。
Resource Owner Password Credentials
該授權方式獲取access token一般只有一步,類似如下GET/POST請求:https://open.xxx.com/oauth2/access_token?client_id=xxxx&client_secret=xxxxx&grant_type=password&username=xxx&password=xxx
OAuth2.0新特性
- 服務器角色區分:授權服務器和資源服務器
- 區別不同的用戶類型、授權場景和授權流程
- 將token分為頻繁傳輸使用但是有效時長較短的Access Token和用於更新Access Token的Refresh Token
- 定義多種token,降低資源請求的構造難度
- 授權過程不簽名,可根據需要采用HTTPS加密傳輸、驗證客戶端密鑰(通過簽名)、客戶端註冊時預先指定callback地址等手段。
- 明確引入客戶端註冊流程:確定Client Type、callback URL以及其它信息
- 引入可選的state參數,幫助客戶端防範CSRF和管理狀態
- 引入scope參數,可以進行授權範圍控制
- token type等可被擴展: bearer(HTTPS), mac(HTTP+sign), etc.
淘寶的OAuth2.0安全控制
- 只支持HTTPS(bears),不支持簽名(簽名是走以前老的授權方式)
- 授權過程指定的callback URL必須與註冊的callback URL在同一個域名,或者為um:ietf:wg:oauth:2.0:oob(表示只顯示授權碼,不回調callback URL)
- 接口分不同級別(R1, R2, W1, W2),同一Token對不同級別接口有不同的有效期
- 接口權限劃分(item, promotion, user, etc.)
說明 為何引入authorization_code?
協議設計中,為什麽要使用authorization_code來交換access_token?這是讀者容易想到的一個問題。也就是說,在協議的第3步,為什麽不直接將access_token通過重定向方式返回給Client呢?比如:
HTTP/1.1 302 Location: https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600
如果直接返回access_token,協議將變得更加簡潔,而且少一次Client與AS之間的交互,性能也更優。那為何不這麽設計呢?協議文檔[1]中並沒有給出這樣設計的理由,但也不難分析:(1) 瀏覽器的redirect_uri是一個不安全信道,此方式不適合於傳遞敏感數據(如access_token)。因為uri可能通過HTTP referrer被傳遞給其它惡意站點,也可能存在於瀏覽器cacher或log文件中,這就給攻擊者盜取access_token帶來了很多機會。另外,此協議也不應該假設RO用戶代理的行為是可信賴的,因為RO的瀏覽器可能早已被攻擊者植入了跨站腳本用來監聽access_token。因此,access_token通過RO的用戶代理傳遞給Client,會顯著擴大access_token被泄露的風險。 但authorization_code可以通過redirect_uri方式來傳遞,是因為authorization_code並不像access_token一樣敏感。即使authorization_code被泄露,攻擊者也無法直接拿到access_token,因為拿authorization_code去交換access_token是需要驗證Client的真實身份。也就是說,除了Client之外,其他人拿authorization_code是沒有用的。 此外,access_token應該只頒發給Client使用,其他任何主體(包括RO)都不應該獲取access_token。協議的設計應能保證Client是唯一有能力獲取access_token的主體。引入authorization_code之後,便可以保證Client是access_token的唯一持有人。當然,Client也是唯一的有義務需要保護access_token不被泄露。 (2) 引入authorization_code還會帶來如下的好處。由於協議需要驗證Client的身份,如果不引入authorization_code,這個Client的身份認證只能通過第1步的redirect_uri來傳遞。同樣由於redirect_uri是一個不安全信道,這就額外要求Client必須使用數字簽名技術來進行身份認證,而不能用簡單的密碼或口令認證方式。引入authorization_code之後,AS可以直接對Client進行身份認證(見步驟4和5),而且可以支持任意的Client認證方式(比如,簡單地直接將Client端密鑰發送給AS)。 在我們理解了上述安全性考慮之後,讀者也許會有豁然開朗的感覺,懂得了引入authorization_code的妙處。那麽,是不是一定要引入authorization_code才能解決這些安全問題呢?當然不是。筆者將會在另一篇博文給出一個直接返回access_token的擴展授權類型解決方案,它在滿足相同安全性的條件下,使協議更簡潔,交互次數更少。
開放平臺鑒權以及OAuth2.0介紹