OAuth 2.0 有 4 種認證流程:
授權碼模式(authorization code)
簡化模式(implicit)
密碼模式(resource owner password credentials)
客戶端模式(client credentials)
下面以微信為例介紹最常見的也是最安全的 Authorization Code認證流程。
一、授權流程說明
微信OAuth2.0授權登錄讓微信用戶使用微信身份安全登錄第三方應用或網站,在微信用戶授權登錄已接入微信OAuth2.0的第三方應用后,第三方可以獲取到用戶的接口調用憑證(access_token),
通過access_token可以進行微信開放平台授權關系接口調用,從而可實現獲取微信用戶基本開放信息和幫助用戶實現基礎開放功能等。
微信OAuth2.0授權登錄目前支持authorization_code模式,適用於擁有server端的應用授權。該模式整體流程為:
獲取access_token時序圖:

二、具體實現過程
下面具體介紹一下微信對這個協議的具體實現過程。
第1步:開發者在微信開放平台申請接入並成功獲取到appid和AppSecret,並配置回調域名。
第2步:構造微信登錄二維碼的超鏈接如下:
參數說明
參數 |
是否必須 |
說明 |
---|---|---|
appid |
是 |
應用唯一標識(前面認證網頁應用中獲得) |
redirect_uri |
是 |
重定向地址,需要進行UrlEncode(前面認證網頁應用中獲得) |
response_type |
是 |
填code |
scope |
是 |
應用授權作用域,擁有多個作用域用逗號(,)分隔,網頁應用目前僅填寫snsapi_login即可 |
state |
否 |
用於保持請求和回調的狀態,授權請求后原樣帶回給第三方。該參數可用於防止csrf攻擊(跨站請求偽造攻擊),建議第三方帶上該參數,可設置為簡單的隨機數加session進行校驗 |
返回說明
用戶允許授權后,將會重定向到redirect_uri的網址上,並且帶上code和state參數
redirect_uri?code=CODE&state=STATE
|
若用戶禁止授權,則重定向后不會帶上code參數,僅會帶上state參數
redirect_uri?state=STATE
|
實際抓包示例:
其中appid參數為開發者在第一步中申請到的appid, scope參數為授權應用的權限列表,redirect_uri為授權成功后的回調地址。
第3步:假如用戶同意授權,在微信登錄成功后會跳轉到redirect_uri參數指定的URL,並在URL尾部追加code參數(即Authorization Code),如上述示例則會跳轉到:
然后,我們可以通過Authorization Code去獲取用戶openid和access_token,進而獲得用戶的信息。
第4步:通過Authorization Code獲取Access Token和openid,服務器端構造如下請求即可獲取Access Token和openid:
參數解釋如下:
grant_type |
授權類型,此值固定為“authorization_code”。 |
client_id |
申請微信登錄成功后,分配給網站的appid。 |
client_secret |
申請微信登錄成功后,分配給網站的appkey。 |
code |
上一步返回的Authorization Code。 |
redirect_uri |
與上面一步中傳入的redirect_uri保持一致。 |
返回說明
正確的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
|
參數說明:
參數 |
說明 |
---|---|
access_token |
接口調用憑證 |
expires_in |
access_token接口調用憑證超時時間,單位(秒) |
refresh_token |
用戶刷新access_token |
openid |
授權用戶唯一標識 |
scope |
用戶授權的作用域,使用逗號(,)分隔 |
unionid |
當且僅當該網站應用已獲得該用戶的userinfo授權時,才會出現該字段。 |
錯誤返回樣例:
{"errcode":40029,"errmsg":"invalid code"}
第5步:使用Access Token以及OpenID來訪問用戶數據
構造如下請求即可訪問用戶數據:
參數說明
參數 |
是否必須 |
說明 |
---|---|---|
access_token |
是 |
調用憑證(上一個請求中獲得) |
openid |
是 |
普通用戶的標識,對當前開發者帳號唯一(上一個請求中獲得) |
lang |
否 |
國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語,默認為zh-CN |
返回說明
正確的Json返回結果:
{
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
|
參數 |
說明 |
---|---|
openid |
普通用戶的標識,對當前開發者帳號唯一 |
nickname |
普通用戶昵稱 |
sex |
普通用戶性別,1為男性,2為女性 |
province |
普通用戶個人資料填寫的省份 |
city |
普通用戶個人資料填寫的城市 |
country |
國家,如中國為CN |
headimgurl |
用戶頭像,最后一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),用戶沒有頭像時該項為空 |
privilege |
用戶特權信息,json數組,如微信沃卡用戶為(chinaunicom) |
unionid |
用戶統一標識。針對一個微信開放平台帳號下的應用,同一用戶的unionid是唯一的。 |
錯誤的Json返回示例:
{ "errcode":40003,"errmsg":"invalid openid" }
三、常見不安全設計造成的風險
風險1:redirect_uri回調域名欺騙
(1)未驗證redirect_uri是否與注冊的回調地址匹配
在上述實現的第二步中將redirect_uri修改為攻擊者控制站點,用戶在授權登錄后將攜帶Authorization Code跳轉到攻擊者控制站點,攻擊者從URL參數中即可獲得Authorization Code並實現用戶劫持。服務端必須驗證client_id(APPID)和redirect_uri規定的域一致,如果不一致則無法登陸。
其實騰訊在實現第三方登錄接入的時候早就考慮過這種老套的攻擊方式,於是,開發者在集成微信登錄時必須在微信開放平台上填寫網站的回調地址,在進行登錄驗證的時候如果redirect_uri中的值與設置好的回調地址不同則會拒絕訪問:
這樣就防止了攻擊者篡改redirect_uri為惡意站點的釣魚攻擊。
但是現在又提出了一種看似合理的繞過方法:
利用合法網站的URL重定向漏洞繞過redirect_uri中的域名白名單限制。
假設我有一個合法的網站whitehat.com,攻擊者控制一個惡意站點hacker.com
攻擊者可以構造這樣一個鏈接來繞過redirect_uri中的域名白名單限制:
http://whitehat.com/index.php?Redirect=http%3a%2f%2fhacker.com%2findex.php
其中Redirect參數指定的為重定向地址
這樣的話,把這個URL地址傳給redirect_uri即可構造一個惡意鏈接,實現用戶授權微信登錄后跳轉到hacker.com
但是用戶的授權令牌Authorization Code真的會被傳送到hacker.com嗎?
我們把上述URL傳給redirect_uri,跳轉到的URL地址如下:
http://whitehat.com/index.php?Redirect=http%3a%2f%2fhacker.com%2findex.php?code=****
細心的人已經發現了,這個鏈接還是跳轉到http://hacker.com/index.php而不是http://hacker.com/index.php?code=****
這是因為code參數前面的&符號沒有URL編碼,因此code參數被whitehat.com處理而不是屬於Redirect參數的一部分。
因此,第一種攻擊模型只能用來構造登錄后的釣魚攻擊,通常情況下Authorization Code不會被傳送到攻擊者控制的站點中。
(2)未設置Authorization Code使用一次就失效
將第二步實現的redirect_uri改為可以引入外鏈的合法URL地址,這樣當合法用戶登錄后加載此頁面的外鏈時,攻擊者就可以從其控制的服務器中在referer消息頭中獲得泄露的Authorization Code。據說這種攻擊方法橫掃國內各大站點,這個攻擊方法在此RFC文檔的Security Considerations中已經提到過:
同時也給出了相應的安全建議:
即Authorization Code在獲取后必須在短時間內失效而且只能被使用一次。這種方法在理論上確實可以有效的阻止上述的攻擊方式,情景分析如下:
(1)攻擊者構造惡意鏈接發送給用戶,其中redirect_uri=http://bbs.test.com/index.php
(2)用戶點擊鏈接登錄后,回調地址為:http://bbs.test.com/index.php?code=****
(3)用戶攜帶code向服務器發送請求加載此頁面,加載的頁面中含有攻擊者放置的外鏈(例如頭像中的圖片鏈接等),用戶加載外鏈中的圖片,攻擊者從referer消息頭中獲得用戶的code
|
由於Authorization Code是通過redirect_uri瀏覽器回調傳輸,容易被截取,服務器生成的臨時Authorization Code必須是一次有效,客戶端使用一次后立即失效並且有效期很短,一般推薦30s有效期,可以保證臨時Authorization Code被客戶端正常消費后不會被再次使用。
風險2:redirect_url XSS跨域攻擊
比如構造一個認證請求,redirect_uri = http://app.com/test?callback=<script src="http://app2.com?getToken.php"></script>
服務器端需要對redirect_uri進行檢查禁止特殊字符輸入,並且對redirect_uri進行全匹配,不做模糊匹配可以杜絕XSS攻擊。
風險3:未添加State 防止CSRF
第2步認證請求url中state參數是最容易被忽略的,大部分IDP不會強制要求客戶端使用state參數。與 CSRF 攻擊類似,如果 state 參數為空,作為攻擊者,
1. 先申請一個新的,專門用於攻擊他人的賬號;
2. 然后走正常流程,跳到微信上去登錄此賬號;
3. 登錄成功之后,微信帶着 code 回跳到第三方站點,如www.test.com,這個時候,攻擊者攔截自己的請求讓他不再往下進行,而直接將帶 code 的鏈接發給受害者,並欺騙受害者點擊;
4. 受害人點擊鏈接之后,繼續攻擊者賬號的登錄流程,不知不覺登錄了攻擊者的賬號
受害者如果這個時候沒察覺此賬號不是他本人的,傳了一些隱私文件,如照片啥的,攻擊者立馬就能通過自己的賬號看到。
而 state 參數如果利用起來,當作 CSRF Token,就能避免此事的發生:
1. 攻擊者依舊獲取 code 並打算騙受害者點擊
2. 受害者點擊鏈接,但因服務器(比如 www.test.com)分配給受害者的設備的 state 值和鏈接里面的(分配給攻擊者的)state 值不一樣,服務器(test.com)直接返回驗證 state 失敗。
所以安全的實現是:
客戶端每次請求生成唯一字符串在請求中放到state參數中,服務端認證成功返回Authorization Code會帶上state參數,客戶端驗證state是否是自己生成的唯一串,可以確定這次請求是有客戶端真實發出的,不是黑客偽造的請求
風險4:Access_Token泄露
- 由於Access_Token是通過http協議從服務器端傳輸給客戶端,為了防止旁路監聽泄露Access_Token,服務器必須提供https來保證傳輸通道的安全性(TSL的要求)
- 客戶端獲取Access_Token,應該在后台與服務端交互獲取Access_Token,不允許Access_Token傳給前端直接使用
- 需要保證Access_Token信息的不可猜測性,以防止被猜測得到
風險5:令牌有效性漏洞
- 維持refresh_token和第三方應用的綁定,刷新失效機制的設計不允許長期有效的token存在;
四、增強OAuth2.0協議設計及使用規范
OAuth2.0協議安全性進行進一步增強。
- 對頒發出去的token權限進行限制,不同用戶申請的token根據人員所屬組織、角色、崗位進行數據隔離
- 對登錄過程安全性增強,對登錄驗證方式進行豐富,支持靜態密碼、手機驗證碼、OTP、生物識別、FIDO
- 對Token頒發后的生命周期管理,可以按策略主動注銷頒發的Token
- 對使用OAuth過程進行行為分析,對登錄過程進行風險識別
- 按照不同應用的安全等級進行分級,不同安全級別應用實現二次認證,保障關鍵系統的安全訪問
參考資料:
https://cloud.tencent.com/developer/article/1447723
https://www.anquanke.com/post/id/98392
https://www.freebuf.com/articles/web/252254.html
https://xz.aliyun.com/t/2260
https://wooyun.js.org/drops/OAuth%202.0%E5%AE%89%E5%85%A8%E6%A1%88%E4%BE%8B%E5%9B%9E%E9%A1%BE.html