現(xiàn)在掃碼登錄是一種很常見的登錄方式。當(dāng)用戶需要登錄某個網(wǎng)站時,網(wǎng)站會提供一種掃碼登錄的方式,用戶打開相應(yīng)的手機(jī)App,掃描網(wǎng)站上顯示的二維碼,然后在App中確認(rèn)登錄,網(wǎng)站監(jiān)測到用戶確認(rèn)登錄后,跳轉(zhuǎn)到登錄成功頁面。從這個形式上看,掃碼登錄就是將用戶在手機(jī)App中的登錄狀態(tài)同步到網(wǎng)站中,這篇文章就來一窺這個同步是如何發(fā)生的。 同一產(chǎn)品中的掃碼登錄假設(shè)有一款產(chǎn)品,這個產(chǎn)品通過手機(jī)端App和PC端應(yīng)用為用戶提供服務(wù),為了方便用戶在PC端上登錄,產(chǎn)品提供了一個掃碼登錄的功能,即PC端應(yīng)用上展示一個登錄二維碼,用戶使用手機(jī)端App掃碼并確認(rèn)登錄,然后用戶就可以在PC端上登錄成功。在這個例子中,手機(jī)端App和PC端應(yīng)用同屬于一個產(chǎn)品,這是一種常見的產(chǎn)品形態(tài),微信、微博、知乎等等都是這種產(chǎn)品形態(tài)的代表。 為了方便介紹,這里再假設(shè)PC端應(yīng)用是一個Web站點(diǎn),下面就來看一下這種登錄方式的運(yùn)作原理: 如上圖所示,整個過程比較簡單,這里大概分為如下幾個步驟: 1、用戶發(fā)起二維碼登錄:此時網(wǎng)站會先生成一個二維碼,同時把這個二維碼對應(yīng)的標(biāo)識保存起來,以便跟蹤二維碼的掃碼狀態(tài),然后將二維碼頁面返回到瀏覽器中;瀏覽器先展示這個二維碼,再按照J(rèn)avascript腳本的指示發(fā)起掃碼狀態(tài)的輪詢。所謂輪詢就是瀏覽器每隔幾秒調(diào)用網(wǎng)站的API查詢二維碼的掃碼登錄結(jié)果,查詢時攜帶二維碼的標(biāo)識。有的文章說這里可以使用WebSocket,雖然WebSocket響應(yīng)比較及時,但是從兼容性和復(fù)雜度考慮,大部分方案還是會選擇輪詢或者長輪詢,畢竟此時通信稍微延遲下也沒多大關(guān)系。 2、用戶掃碼確認(rèn)登錄:用戶打開手機(jī)App,使用App自帶的掃碼功能,掃描瀏覽器中展現(xiàn)的二維碼,然后App提取出二維碼中的登錄信息,顯示登錄確認(rèn)的頁面,這個頁面可以是App的Native頁面,也可以是遠(yuǎn)程H5頁面,這里采用Native頁面,用戶點(diǎn)擊確認(rèn)或者同意按鈕后,App將二維碼信息和當(dāng)前用戶的Token一起提交到網(wǎng)站API,網(wǎng)站API確認(rèn)用戶Token有效后,更新在步驟1中創(chuàng)建的二維碼標(biāo)識的狀態(tài)為“確認(rèn)登錄”,同時綁定當(dāng)前用戶。 3、網(wǎng)站驗證登錄成功:在步驟1中,二維碼登錄頁面啟動了一個掃碼狀態(tài)的輪詢,如果用戶已經(jīng)“確認(rèn)登錄”,則輪詢訪問網(wǎng)站API時,網(wǎng)站會生成二維碼綁定用戶的登錄Session,然后向前端返回登錄成功消息。這里登錄狀態(tài)維護(hù)是采用的Session機(jī)制,也可以換成其它的機(jī)制,比如JWT。 為了保證登錄的安全,有必要采取一些安全措施,可能包括以下若干方法:
第三方應(yīng)用的掃碼登錄現(xiàn)在很多網(wǎng)站除了提供自身賬號的登錄方式以外,還提供了微信掃碼登錄、微博掃碼登錄等方式,本來這對用戶來說是十分方便的,不過很多站點(diǎn)為了獲取用戶手機(jī)號,首次登錄時還需要用手機(jī)驗證碼再登錄一次,用戶會有點(diǎn)被欺騙的感覺,不過這個問題不是本文要探討的。 這里以微信掃碼登錄某網(wǎng)站為例,某網(wǎng)站就是第三方應(yīng)用,所謂第三方是相對微信自身來說的。相比同一產(chǎn)品中的掃碼登錄,網(wǎng)站使用微信掃碼登錄會更復(fù)雜一些,因為這涉及到不同應(yīng)用之間的登錄安全。下面就給出這一登錄方式的詳細(xì)運(yùn)作原理,時序圖比較長,不過只要耐心點(diǎn)就能完全搞清楚,甚至自己實現(xiàn)一個類似的第三方掃碼登錄方案。 這里對一些關(guān)鍵點(diǎn)特別說明下: 1、步驟3 生成微信登錄請求記錄:當(dāng)用戶掃碼并同意登錄之后,步驟25中瀏覽器會重定向到第三方應(yīng)用,如果之前沒有創(chuàng)建一條登錄請求記錄,網(wǎng)站并不能確定這次登錄就是自己發(fā)起的,這可能導(dǎo)致跨站請求偽造攻擊。比如使用某個應(yīng)用的微信登錄二維碼,騙取用戶的授權(quán),然后最終回調(diào)跳轉(zhuǎn)到其它站點(diǎn),被回調(diào)站點(diǎn)只能被動接受,雖然下一步驗證授權(quán)Code通不過,微信也可能會認(rèn)為第三方應(yīng)用出了某種問題,搞不好被封掉。因此第三方應(yīng)用創(chuàng)建一條登錄請求記錄之后,還要把記錄的標(biāo)識拼接到訪問微信登錄二維碼的url中,微信會在用戶同意登錄后原樣返回這個標(biāo)識,步驟26中第三方應(yīng)用可以驗證這個標(biāo)識是不是有效的。 2、步驟17 顯示應(yīng)用名稱和請求授權(quán)信息:因為微信支持很多的第三方應(yīng)用,需要明確告知用戶正在登錄哪個應(yīng)用,應(yīng)用可以訪問自己的什么信息,這都是用戶做出登錄決定的必要信息。因此掃碼之后,微信手機(jī)端就需要去微信開放平臺查詢下二維碼對應(yīng)的第三方應(yīng)用信息。 3、步驟24 登錄臨時授權(quán)Code:微信開放平臺沒有直接向瀏覽器返回登錄用戶的信息,這是因為第三方應(yīng)用還需要對用戶進(jìn)行授權(quán)并保持會話的狀態(tài),這適合在應(yīng)用的服務(wù)端來處理;而且直接返回用戶信息到瀏覽器也是不安全的,并不能保證二維碼登錄請求就是通過指定的第三方應(yīng)用發(fā)起的。第三方應(yīng)用會在步驟27中攜帶這個授權(quán)Code,加上應(yīng)用的AppId和AppSecret,再去向微信開放平臺發(fā)起登錄請求,臨時授權(quán)Code只能使用1次,存下來也不能再用,且只能用在指定的應(yīng)用(即綁定了AppId),AppSecret是應(yīng)用從服務(wù)端提取的,用來驗證應(yīng)用的身份,這些措施保證了微信授權(quán)登錄的安全性。不過驗證通過后還是沒有直接返回用戶信息,而是返回了一個access token,應(yīng)用可以使用這個token再去請求獲取用戶信息的接口,這是因為開放平臺提供了很多接口,訪問這些接口都需要有授權(quán)才行,所以發(fā)放了一個access token給第三方應(yīng)用,這種授權(quán)登錄方式叫做OAuth 2.0?;诎踩矫娴目紤],access token的有效期比較短,開放平臺一般還會發(fā)放一個refresh token,access token過期之后,第三方應(yīng)用可以拿著這個refresh token再去換一個新的access token,如果refresh token也過期了或者用戶取消了授權(quán),則不能獲取到新的access token,第三方應(yīng)用此時應(yīng)該注銷用戶的登錄。這些token都不能泄漏,所以需要保存在第三方應(yīng)用的服務(wù)端。 這里有一個有意思的問題:為什么微信的二維碼登錄頁面Url中沒有第三方應(yīng)用的簽名? 以極客時間的這個微信登錄二維碼頁面的Url為例: 其中appid是微信開放平臺分配給極客時間的應(yīng)用Id;redirect_uri是用戶授權(quán)登錄后微信回調(diào)的極客時間url,雖然看起來很長,其實就是在極客時間內(nèi)的頁面之間跳來跳去;state是極客時間生成的一個登錄id,不同的state會生成不同的二維碼,微信回調(diào)極客時間的時候會帶著這個state。 這個url中只有極客時間的appid,沒有極客時間的簽名,也就是說微信會生成極客時間的登錄二維碼,但是不驗證這個二維碼登錄請求是不是極客時間發(fā)起的,那么任何人都可以生成極客時間的微信登錄二維碼頁面。這好像不太嚴(yán)謹(jǐn)。 這樣安全嗎?攻擊者可以很簡單的獲取某個應(yīng)用的微信登錄二維碼,然后再騙取用戶的登錄授權(quán)(這個也相對簡單,弄個假冒的網(wǎng)站,或者直接話術(shù)忽悠,用戶很可能不仔細(xì)看掃碼確認(rèn)的內(nèi)容),再通過瀏覽器攔截或者DNS攻擊獲取到臨時授權(quán)Code,直接越過檢查應(yīng)用State,前邊這些都相對容易。但是向微信驗證臨時授權(quán)Code的時候必須攜帶App Secret,這個就很難了,除非第三方應(yīng)用開發(fā)者自己泄漏了這一核心機(jī)密。對于用戶信息的保護(hù),整體上來說是安全的,攻擊者基本上拿不到訪問用戶信息的access token。如果在url中加上這個簽名,對用戶信息的保護(hù)作用也沒有加強(qiáng),授權(quán)Code還是很容易獲?。欢液灻话阋彩且蕾嘇pp Secret,如果泄漏了App Secret,簽名也就失去了意義。 如果有人不斷地生成某個應(yīng)用的微信登錄二維碼怎么辦?這是其他類型的攻擊了,微信可以采取一些限流措施,甚至直接封掉某些IP,這對正常用戶沒什么影響。 微信二維碼登錄的變種微信除了跳轉(zhuǎn)到二維碼登錄頁面的方式,還提供了第三方網(wǎng)站中內(nèi)嵌二維碼的方式。通過微信JS SDK生成二維碼并在網(wǎng)頁的指定區(qū)域展現(xiàn),用戶掃碼同意登錄后,微信JS SDK發(fā)起重定向或者在iframe中打開應(yīng)用回調(diào)頁面,傳遞臨時授權(quán)Code和應(yīng)用State,后續(xù)的流程就都一樣了。 另外這里的第三方應(yīng)用如果是移動端App,微信開放平臺也支持掃碼登錄的方式,區(qū)別在于需要集成微信SDK,獲取二維碼和接收用戶授權(quán)Code都是通過SDK回調(diào)的方式,后續(xù)的流程也都一樣。 以上就是本文的主要內(nèi)容了,鑒于本人才疏學(xué)淺,如有錯漏歡迎指正。
|
|