OAuth2.0安全設計之Authorization Code

OAuth 2.0 有 4 種認證流程:javascript

  • 受權碼模式(authorization code)
  • 簡化模式(implicit)
  • 密碼模式(resource owner password credentials)
  • 客戶端模式(client credentials)

下面以微信爲例介紹最多見的也是最安全的 Authorization Code認證流程。php

1、受權流程說明

       微信OAuth2.0受權登陸讓微信用戶使用微信身份安全登陸第三方應用或網站,在微信用戶受權登陸已接入微信OAuth2.0的第三方應用後,第三方能夠獲取到用戶的接口調用憑證(access_token),
經過access_token能夠進行微信開放平臺受權關係接口調用,從而可實現獲取微信用戶基本開放信息和幫助用戶實現基礎開放功能等。
  微信OAuth2.0受權登陸目前支持authorization_code模式,適用於擁有server端的應用受權。該模式總體流程爲:html

       獲取access_token時序圖:前端

    
 
 

2、具體實現過程

下面具體介紹一下微信對這個協議的具體實現過程。java

第1步:開發者在微信開放平臺申請接入併成功獲取到appid和AppSecret,並配置回調域名。web

第2步:構造微信登陸二維碼的超連接以下:json

參數說明
參數
是否必須
說明

appidapi

應用惟一標識(前面認證網頁應用中得到)

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

實際抓包示例:

https://open.weixin.qq.com/connect/qrconnect?response_type=code&appid=wx2198c66352420194&redirect_uri=https%3A%2F%2Fpassform.test.com%2Fv3%2Fweb%2Flogin%2FwechatCallBack%3FisPc%3D1%26randomNum%3D%26redirect_uri%3Dhttps%253A%252F%252Fwww.test.com%252F%253Fopenid%253D33336839a7398ce8%26client_id%3D30&scope=snsapi_login&state=1614336736067

其中appid參數爲開發者在第一步中申請到的appid, scope參數爲受權應用的權限列表,redirect_uri爲受權成功後的回調地址。

 
 

第3步:假如用戶贊成受權,在微信登陸成功後會跳轉到redirect_uri參數指定的URL,並在URL尾部追加code參數(即Authorization Code),如上述示例則會跳轉到:

https://passform.test.com/v3/web/login/wechatCallBack?isPc=1&randomNum=&redirect_uri=https%3A%2F%2Fwww.test.com%2F%3Fopenid%3D33336839a7398ce8&client_id=30&code=053isZFa12PMAA0NVeGa1yR9300isZFe&state=1614336736067

而後,咱們能夠經過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",
     "privilege":[
         "PRIVILEGE1",
         "PRIVILEGE2"
     ],
     "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
 
 

參數

說明

openid

普通用戶的標識,對當前開發者賬號惟一

nickname

普通用戶暱稱

sex

普通用戶性別,1爲男性,2爲女性

province

普通用戶我的資料填寫的省份

city

普通用戶我的資料填寫的城市

country

國家,如中國爲CN

headimgurl

用戶頭像,最後一個數值表明正方形頭像大小(有0、4六、6四、9六、132數值可選,0表明640*640正方形頭像),用戶沒有頭像時該項爲空

privilege

用戶特權信息,json數組,如微信沃卡用戶爲(chinaunicom)

unionid

用戶統一標識。針對一個微信開放平臺賬號下的應用,同一用戶的unionid是惟一的。

錯誤的Json返回示例:
{ 
     "errcode":40003,"errmsg":"invalid openid"
}

3、常見不安全設計形成的風險

風險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存在;

4、加強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
相關文章
相關標籤/搜索