oauth授權協議的原理


 

http://oauth.net/2/ 協議的原文。原來是1.0版本,現在是2.0版本了

 

https://ruby-china.org/topics/15396

https://blog.yorkxin.org/posts/2013/09/30/oauth2-1-introduction/

通俗解釋:

http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

 

要解決的問題:

 

 

獲取授權。每次登錄,都要讓用戶進行授權。

 

 現實中的例子,只有用戶同意授權給媽媽網,騰訊才發放一個令牌給媽媽網,媽媽網憑借這個令牌去騰訊獲取用戶的信息。

 

那么就意味着說,令牌的生成規則要跟用戶和媽媽網的信息綁定關系。

 

奇怪,如果這個令牌被泄漏呢?別人也可以憑借這個令牌拿到信息啊。現實生活中,別人即便拿到你的令牌(票券),的確是可以去通行

 

但是現實生活中發現了這個漏洞后,會增加一些安排判斷,比如車票,別人就算偷到,撿到車票也不能去乘車,還要比對一下是否與身份證號碼匹配。

 

除非你拿到對方的身份證了。這樣把安全系數提高。發生的概率就會減少。就算你拿到對方的身份證,有時候還會看看你本人樣子是否與身份證照片相差很大。

 

在我們這一層,你在url中是可以看到key,沒錯。但是你得同時拿到密鑰才行。需要密鑰對數據進行簽名的。簽名失敗是不允許訪問的。

 

 

騰訊的微信是這樣子:

1、用戶授權后,則生成一個臨時性的code返回給客戶端

code如何生成的呢?參考了oauth2.0協議

 

該碼與客戶端ID和重定向URI,是一一對應關系。

 

客戶端id。重定向的uri,是早就預定義好了。

 

請求騰訊去獲取code的時候,會帶兩個關鍵性的參數:騰訊的appid(就是oauth2.0協議中的客戶端id)、redirect_uri(騰訊要回跳回來的地址)

 

現在思考:為什么要驗證redirect_uri是否與后台填寫的域名一致呢。其實這樣可以增加安全性。

 

我只會重定向到指定的url去。這樣會更加安全。不會重定向到攻擊者指定的url去。

qq登錄的時候,還為了更加安全,做全路徑判斷了。以前只是同一個域名下即可了。后來為了安全。必須后台填寫完全相同的url地址。

 

比如后台配置必須是這樣的完整url: www.abc.com/qqlogin/callback.php

 

是域名www.abc.com是不行,以前是可以的。

 

那么會完全驗證這個url是否一致。否則就拒絕掉。

 

這對於passport的參考意義:所有接入passport的子應用,我是不是可以限制回調地址呢?限制在指定域名下就可以。

 

目前來看,還不需要這樣子做。沒有遇到問題?

 

 

2、憑借appid和臨時性的code去獲取access_token

 

 

3、憑借access_token和用戶的openid去獲取用戶的信息(昵稱,頭像等等)

 

 

通俗例子:

 

現在想要實現qq登錄。

一種辦法是:讓用戶把對方的帳號和密碼發給a網站,讓a網站去登錄qq。那這樣就不安全了,因為密碼不能泄漏給這些網站。

 

把帳號和密碼直接給a網站,a網站就能獲取到qq的所有信息的權力了。這樣子不安全。

 

現在明白原因了,為什么要設計一個用戶的access_token,在每個網站不同。就是為了安全性。假設一個用戶的access_token在a網站是一樣的,在b網站也是一樣的。

 

那么,泄漏了,就麻煩了。因為我可以獲取用戶的所有資料了。

 

這個access_token可以理解成令牌。

 

我在思考,我應該如何設計這種模式呢?

 

我可以畫序列圖,這樣子清楚解釋。

 

 

關鍵是這個access_token,這樣子應用就不需要獲取用戶的密碼了。完全通過這個授權層來實現登錄。

 

我覺得code 與access_token是建立了一個映射關系的。

 

access_token則是與用戶的openid建立了映射關系的。

 

access_token  openid(根據appid和qq進行生成)

 

 

其實我在返回ticket的時候,完全可以增加一個字段expire,表示過期時間。這樣子他們自己可以根據去刷新自己的cookie時長?

 

子應用和passport系統登錄時間不一致,的確會導致一些用戶體驗不好。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

靠近大家的習慣。oauth2.0這樣的模式去。

 

獲取臨時性的code。然后憑借code獲取ticket(類似於access_token)。

 

每次請求都必須要帶上passport授予的key在url中。

 

 

思考:qq,微信這些實現oauth2.0登錄的時候,為什么除了帶上screct還要帶上appid呢?

 

微信登錄,只需要帶上appid,就可以訪問微信了。關於安全性,不過最終都要經過用戶的授權:用戶的微信帳號必須是登錄狀態。即便是登錄狀態,也要經過用戶授權訪問。

 

1、跳轉到微信的授權登錄頁面去,不需要screct。因為這個需要用戶授權才能操作。不涉及到安全性。之所以要傳遞一個appid在url中給微信,微信可以知道用戶要登錄哪個應用而已,方便做提醒"你正在准備登錄媽媽網"

 

2、通過code去獲取access_token,需要screct的原因是,這里涉及到安全性了。

 

 

為什么要設計一個refresh token?

 

怕access_token過期。

 

為什么要增加這個refresh token去延長access_token的有效期呢?

 

 

客戶端:指的就是第三方應用程序。比如網站。

服務商:如何理解呢?通俗點理解呢。

資源所有者:指用戶。這些資源是用戶的,比如個人信息,保存的資料都是用戶的。

 

 

 

 

 

 

state 用於保持請求和回調的狀態,授權請求后原樣帶回給第三方。該參數可用於防止csrf攻擊(跨站請求偽造攻擊),建議第三方帶上該參數,可設置為簡單的隨機數加session進行校驗

 

 

 

 

這個state指是可以網站自己設置的參數,qq那邊只是原封不動的返回來。據說這樣可以避免csrf攻擊。

不太理解。但是可以模仿。oauth官網文檔是推薦填寫這個的。既然是為了避免csrf攻擊,偽造請求。

 

那么state的值就不要是一個固定的值了,因為可以偽造。所以要設置成一個動態變化的值。

比如可以:時間戳+隨機數。時間戳是可以偽造的(用其他語言生成一個,時間只要同步就可以)。但是加上隨機數。就很難確定是哪個隨機數了。因為隨機數每次都是變化的,對方就算模擬也很難模擬出恰好生成同一個隨機數出來,它是在一個范圍內變化的。

 

思考:為什么單獨只需要在瀏覽器重定向(比如騰訊回跳到自己網站)的時候,附加上state返回呢?

其他通過access_token獲取用戶信息,這些都是服務器之間的通信。很難被截取到。

但是獲取臨時性code這一步,是在瀏覽器回跳的時候,別人也可以重定向這個地址,偽造請求。

 

現在要加上一個每次都變化的值會比較安全點。到底是如何攻擊的呢?

 

 

 

 

 

passport倒未必需要這樣子,我覺得passport調用方是自己的應用。不是外部應用。是自己公司的。是這樣的嗎?

 

 我現在可以設置一個時間戳,然后緩存在session中。qq會返回過來。如果返回來的值不一樣,則提示頁面已過期。

 

 

 

 

 

 

 

 

 

 

 這是網頁版登錄的正式文檔:

https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN

 

弄錯了,結果跑到公眾帳號平台去了:

http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code

 

難怪提示,必須要使用客戶端打開。

 

實際上過程是:二維碼。掃描后就可以進行登錄了。

 

 

 

我現在想理解一下,這個openid,unionid是根據什么生成的呢?

如何生成的呢?

 

同一個qq號,每個應用id對應的openid會不同。

 

容易把人給搞暈:access_token

openid之類的。

 

根據access_token才能訪問。access_token存在有效期。附加上access_token和openid才能拿到指定用戶的信息

 

 

 

 

 

 目前騰訊分配給我們的appid是一個特殊的appid,由於是特殊性。在騰訊的后台查詢不到,騰訊並沒有單獨提供后台給我們,直接修改回調地址。只能聯系對方的技術人員修改

 

  關於當時的背景:理解下面知識點

 

   他們對一個用戶(qq)的openid值是根據應用的appid生成的,也就是說:QQ號12348899,換一個appid,這個qq號對應的openid值就不同了。

   這樣子設計是為了安全考慮。否則不同的應用(比如a公司和b公司的應用),只要知道一個qq號碼,就能獲取到它的openid去其他地方使用。或者保存到自己的數據庫里面。

   現在目的要保證不一樣,騰訊每個qq的openid值根據應用不同,而不同的。

 

  媽媽網當時為了解決這樣的問題:

    由於32論壇的域名,都是不一樣的一級域名,比如www.gzmama.cn、www.cdmama.cn。根據騰訊的qq機制,那么同一個qq用戶在這些應用對應的openid值就不一樣了。

    想實現32論壇的qq用戶,表現得是同一個openid。根據上面對騰訊openid的介紹,騰訊原來的機制無法滿足。所以我們當時要求騰訊為我們單獨開設了一個appid。

    此appid是一個特殊的appid。把很多的應用合在一起。

 

 

 

 

 

 

 

 

同意授權,實際上就是根據用戶的登錄信息和appid生成一個code嗎?這個code於這些信息是關聯的。

 

回跳回來的時候,帶這個code,憑借這個code可以獲取access_token

 

 

我以前以為,獲取到access_token,獲取到code會不安全。但實際上授權的模式已經避免密碼泄漏的問題了。

 

對方如何才能獲取到code,code只是一個橋梁,一個中間作用。用完一次就刪除了。

 

關鍵是對方怎么攻擊呢?

 

我們的目的,就是要避免攻擊者去獲取用戶的資源:照片,資料等信息。

 

必須通過用戶臨時授權,每授權一次才能進一步操作。

 

實際上可以直接返回access_token給客戶端。但是不安全。必須要通過code來獲取。

 

要隱藏。在服務器之間進行通信,這樣很難抓到包。

 

我只要拿到access_token,那么也是可以獲取用戶信息了。

 

請求的時候帶scope參數,這樣子授權服務器就知道這個應用打算申請什么權限,這樣可以提醒用戶:你將要授權進行干嘛,你是否同意。

 

 

在oauth2.0協議中,提供了好幾種模式。其中一種模式是直接把access_token放到url中返回給客戶端了。這種是簡化模式。存在token被泄漏的風險。

 

 

 

 

 

 

 "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value"

要有訪問令牌access_token。訪問令牌有個過期時間。如果要延長,就要使用refresh_token來進行刷新。


access_token有效期是2個小時。refresh_token有效期是30天。


在qq中,Access_Token的有效期默認是3個月,過期后需要用戶重新授權才能獲得新的Access_Token。


這是php版本的 演示:
http://brentertainment.com/oauth2/lockdin/authorize?response_type=code&client_id=demoapp&redirect_uri=http%3A%2F%2Fbrentertainment.com%2Foauth2%2Fclient%2Freceive_authcode&state=6507ae4073f82d4a228eeef1a5af22d6#

https://github.com/bshaffer/oauth2-demo-php 這是官方提供的各個語言的demo


access_token與openid有什么區別呢?完成的意義有什么不一樣呢?


在oautho2.0協議原文中,只有access_token,並沒有openid的概念。所以openid只是在此基礎上加的一個概念。







延長令牌,實際上是重新生成一個令牌: 重新生成refresh_token和access_token,先判斷這個refresh_token是不是合法就行了。



access_token和openid都要實時生成。

使用一種算法來生成。這種算法必須是可逆的。也就是根據access_token能夠反解出uid出來。openid的值也是要一樣的。

使用一個密鑰來生成。

desc算法嗎?


這篇文章比較好講述了openid和access_token的區別

http://desert3.iteye.com/blog/1701626





關於access_token被盜用的問題思考

想想現實生活中的例子,如果你撿到別人的火車票后,直接可以去乘車,以前是沒有身份認證。我只認車票就放行。

這個動作就類似於,拿了access_token。

現在我們的思路與車票增加驗證是一樣的思路,是增加驗證,把門檻提高。

具體這樣做:你拿到access_token還不夠。還需要傳遞密鑰來驗證才行。

就算你能偷到access_token還不夠,你還得知道密鑰才行。

現在明白access_token的有效期不是安全的關鍵因素。短一點的確是比長更加安全。

安全的核心因素是什么呢?就是增加障礙物,門檻增加。

考慮到用code交換access_token是服務器之間的交互。code是容易被知道,但是密鑰很難知道。token交換的時候是服務器之間的通信了。

總結:思路就是增加多幾道的驗證門檻。



access_token准確的理解應該是這樣:

是用戶對第三方應用進行授權,授權可以在第三方應用進行登錄。比如媽媽網的用戶,可以在掛號網站進行登錄。這需要用戶進行授權才行。

授權過的用戶會生成一個access_token,這個access_token要與應用id和用戶id進行關聯(對應關系存儲起來)

這句話理解到了本質;OAuth可以把提供的Token,限制在一個網站特定時間段的的特定資源。





為什么通過access_token就能直接去獲取用戶的信息呢?根本不需要傳遞密鑰去。

騰訊和微博在這方面是一樣的模式。怎么來理解呢?
難道是基於這樣一個假設:access_token不能隨便被拿到。需要門檻。所以如果你能夠拿到,那就說明你是正常的用戶。實際上,我覺得如果是這樣的理解,這種理解就是不夠全面的。



把client_id和client_secret看成就是這個app訪問授權中心的帳號和密碼,這么來理解會更加通俗點


這里解釋了,為什么access_token不要時間太長。

短期token和長效的身份憑據 - 原先的OAuth,會發行一個 有效期非常長的token(典型的是一年有效期或者無有效期限制),在OAuth 2.0中,server將發行一個短有效期的access token和長生命期的refresh token。
這將允許客戶端無需用戶再次操作而獲取一個新的access token,並且也限制了access token的有效期。

通俗點說,access_token是故意設置比較短的時間。然后增加一個擁有長有效期的refresh_token,來延長access_token的。




access_token的生成方式?

最好是不要在 token 字串附上這些信息,即使是 hash 過的也不好,因為這樣子就有機會可以假造 token 。要把 token 字串視為一個參照,服務器用這個 token 字串去查到真正的授權內容
也就是說,access_token從安全角度來說,還是要用數據進行存儲的。





Access Token 是一個字串,記載了特定的存取範圍 (scope) 、時效等等的資訊。
















兩次的access_token:

8576C24EEA96E6AC8B035C375A47C922


8576C24EEA96E6AC8B035C375A47C922



兩次的刷新token:

5FC5A99DA4774E7E3569B8BDC1170138

5FC5A99DA4774E7E3569B8BDC1170138



B03CD9A2AF5BA4CDCEA541366EFF995E

52C78B6D6F1D10979B1ED46B5F8709A2


每一次一個code。





通過openid獲取用戶信息的接口思考


想要獲取用戶的資料,通過傳遞openid來獲取。但是還不夠。如果知道openid被泄漏了。那么就可以獲取用戶資料,不安全了。

所以還要增加一個門檻,要傳遞一個access_token。先驗證access_token的合法性。access_token帶有失效期。是與client_id和user_id關聯了。
如果access_token合法,就能查詢到對應的client_id和user_id的信息。
那么user_id知道了就方便查詢哪個用戶信息了。並且,我們還要驗證openid的合法性。openid是根據user_id和client_id換成一定算法生成出來的。
所以查詢出的client_id和user_id,就能用算法算出openid,假設是openid_b,與傳遞過來的openid_a對比,是否一致。

不一致,就要報錯:openid錯誤。

總共兩個錯誤:access_token錯誤和openid錯誤。



思考:

發現要研究,就把一些東西給研究透徹。然后自己能夠回答一些關鍵性的疑惑。拋出一些問題來,才能解釋掉。

比如oauth,不用輸入帳號和密碼就可以了。

以為有個access_token就可以。

那豈不是沒輸入帳號和密碼就能拿到access_token。





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM