證明與計算(6): 身份認證與授權


目錄:

** 0x00 引子
** 0x01 用戶名和密碼
** 0x02 密碼管理器的基本原理
** 0x03 多因素認證
** 0x04 雙因素認證(two-factor-auth)的基本原理。
** 0x05 [OpenID] vs [OAuth]
** 0x06 [IDToken] vs [JWT, JWS, JWE]
** 0x07 如何生成強密碼
** 0x08 OpenGPG

0x00 引子

每天,你使用用戶名和密碼登錄網站。
每天,你使用手機驗證碼重置密碼。
每天,你使用微信掃碼登錄其他軟件。
每天,你使用微信授權第三方小程序訪問你的個人信息。
每天,你可能會對如何記住眾多密碼感到棘手。

作為開發者,你應該對安全地做“身份認證”和“授權”,有十分清晰的認識。

0x01 用戶名和密碼

那么,如何安全地保存密碼?

  1. 客戶端,為每個用戶,使用密碼學安全的偽隨機數生成器(Cryptographically secure pseudorandom number generator)生成隨機密碼password。通過https等安全通道傳輸username、password到服務端。
  2. 服務端,為每個用戶,生成一個獨立的隨機鹽salt,加鹽是為了防御彩虹字典破解。
  3. 使用標准的加密哈希函數計算密碼和鹽的的哈希: hashValue=hash(password+salt)
  4. 加密哈希函數應該選擇慢哈希函數(Slow Hash Function),慢加密哈希函數可以是:Argon2, bcrypt, scrypt, or PBKDF2 中的一種,不要自己發明哈希函數。不要使用快哈希函數(Fast Hash Function),例如MD5, SHA1, SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3。使用慢加密哈希函數是為了防御暴力計算破解。
  5. 在數據庫中保存匹配的<username, hashValue, salt>

以及,如何安全地校驗密碼?

  1. 客戶端,通過https、ssh等安全通道傳輸username、passowrd到服務端。
  2. 服務端,從數據庫中取出<username, hashValue, salt>數據
  3. 使用和保存時一樣的hash函數計算 hashValue'=hash(password+salt)
  4. 將計算結果hashValue'和保存的hashValue比較,如果一致則校驗通過,否則失敗。

最后,密碼忘記了怎么辦?
0. 只能重置密碼!

  1. 用戶輸入郵件,如果郵件存在,則發送重置鏈接給那個郵件,如果不存在,不要發送!
  2. 用戶輸入手機號碼,通過驗證碼來驗證,參考下面的雙因素認證一節。

[1] 密碼學安全偽隨機數生成器
[2] Cryptographically secure pseudo-random number generator, CSPRNG
[3]
[4] Salt
[5] Origin of the term "salt" in computer security
[6] hashing-security
[7] Everything you ever wanted to know about building a secure password reset feature
[8] The Definitive 2019 Guide to Cryptographic Key Sizes and Algorithm Recommendations

0x02 密碼管理器的基本原理

密碼很多怎么辦?用戶可以選擇使用密碼管理器來管理自己的密碼。那么,密碼管理器的基本原理是怎樣的呢?

  1. 使用一個包含字母,數字,特殊字符的8位以上的強主密碼(masterpassword)。
  2. 使用主密碼+用戶名等只有你自己知道的信息,通過一個密碼哈希函數去生成一個鹽(salt)。所謂密碼哈希函數就是是滿足單向性質(計算哈希容易,反向破解很難,具體來說是NP難度),強碰撞防御的哈希函數。
  3. 有了主密碼和鹽,然后可以使用PBKDF2,Bcrypt, Scrypt ,Argon2中的一種Key Derived function(KDF)來生成一個密鑰 aesKey = kdf(masterpassword,salt),此時有3個重要的數據:masterpassword, salt,aesKey,這3個數據不會上傳到雲端。
  4. 每當注冊一個新站點需要一個新密碼時,使用密碼管理器生成每個站點用的隨機字符串強密碼,一般是 xxxx-xxxx-xxxx-xxxx格式,形成<website,xxxx-xxxx-xxxx-xxxx>數據。這個生成過程必須要用密碼安全的隨機數生成器。
  5. 密碼管理器使用步驟3生成的aesKey,采用AES對稱加密算法去加密第4步生成的站點密碼數據,上傳加密后的密碼數據到雲端。
  6. 在其他硬件上登錄密碼管理器客戶端后(最好登錄客戶端的密碼與主密碼應該不一樣),客戶端同步加密后的數據。
  7. 需要密碼時輸入主密碼(masterpassword),使用主密碼+用戶名+同樣的單向哈希函數生成鹽(salt),通過KDF實時生成aeskey,再用aeskey去解密加密后的數據的到<website,xxxx-xxxx-xxxx-xxxx>。注意,這里用到的主密碼是你自己記住的,鹽(salt)和aesKey是實時在本地生成的,這也是密碼管理器可安全使用的一個關鍵因素,別人即使獲得了加密后的數據,他沒有你的主密碼,用戶名等私人信息,他是很難破解的。這也是為什么主密碼本身應該足夠強,需要包含字母,數字,特殊字符以及8位以上的原因。
  8. 如果不同步到雲端,那么切換到其他設備時,你要自己手工拷貝加密后的密碼數據到新設備的密碼管理器里。
  9. 同步到雲端的密碼管理器有LastPass/1Password,不同步到雲端的密碼管理器早期的1Password,現在是否有可選模式不知道,整體上來說許多用戶反饋1Password的加密過程更可靠,如果你要選擇和對比,可進一步分析。如果你用的是Mac系統,使用safari瀏覽器內置的密鑰管理器記住密碼,並使用icloud同步到iphone,也是屬於會同步到雲端的密碼管理器,只是它只支持蘋果自己的系統。

[1] Password Manager
[2] 1Password
[3] Lastpass
[4] password-manager-hacking

0x03 多因素認證

我們知道密碼在認證身份中的重要作用,但實際上密碼只是驗證身份的一個因素。身份認證實際上分為:

  1. 只驗證密碼的單因子認證,驗證的是一個人“只有自己知道”的知識。
  2. 除了驗證密碼,還驗證只有你個人持有的東西,例如手機,u盾,微信掃碼認證,Google設備驗證器等。驗證的是一個人“只有你擁有的”設備。這就形成了雙因子認證。即使都是雙因素認證,限制級別不同,也會導致不同級別的安全,例如Google在雙因素認證的基礎上,又提供❝ 高級保護計划❞服務,用戶必須購買兩個安全密鑰:一個是具備無線網絡功能的主密鑰,另一個是備用密鑰。然后只能在安全密鑰設備上訪問Google賬號,以及訪問白名單內的OAuth服務。 (參考[3])
  3. 進一步,還可以通過生物特征驗證你是誰。例如,指紋,人臉識別等,銀行客戶端和支付寶等就會有這種認證。那么就形成了三因子認證

[1] WebAuthn
[2] Web Authentication: What It Is and What It Means for Passwords
[3] Google Advanced Protection

0x04 雙因素認證(two-factor-auth)的基本原理。

雙因素認證的核心目的是確認只有用戶擁有的設備,例如忘記密碼后通過手機驗證碼可以快速重置密碼,密碼+手機就構成了一個雙因素認證。而像Google Authenticator則是一個雙因素認證的App。雙因素認證的基本過程是怎樣的呢?

  1. 個人手機上安裝一個生成雙因素認證碼的App,例如:Google Authenticator App。
  2. 登陸需要支持雙因素認證的網站,該網站生成一個內含共享密鑰(shared_secret)的二維碼,這個步驟叫做初始安裝,每個要使用雙因素認證的網站都需要和用戶的Google Authenticator做這個安裝步驟。
  3. 手機上的Google Authenticator通過掃描的方式,安全地將網站共享給個人的密鑰導入App。
  4. 手機上的Google Authenticator通過HMAC(shared_secret, 當前Unix時間/30秒取整)生成一個20字節的值。
  5. 對這20字節的值再做處理,最后得到一個6位數字整數,這個整數是一個一次性密碼。
  6. 在網站上輸入這6位數字整數的一次性密碼。
  7. 網站上使用和Google Authenticator共享的密鑰shared_secret,同樣計算HMAC(shared_secret, 當前Unix時間/30秒取整),並處理后得到同樣的6位一次性密碼。
  8. 網站通過校驗用戶輸入的一次性密碼和自己生成的一次性密碼,確認用戶確實擁有這台手機。
  9. 共享密碼只在第一次的時候需要通過掃碼的方式從網站共享給了個人手機上的驗證器。
  10. 這里生成一次性密碼的算法叫做Time-Based One-Time Password Algorithm,也就是TOTP。

[1] 2-Step Verification with Google Authenticator
[2] HOTP: An HMAC-Based One-Time Password Algorithm
[3] TOTP: Time-Based One-Time Password Algorithm
[4] How Google Authenticator Works, Github: Google Authenticator
[5] Microsoft Authenticator

0x05 [OpenID] vs [OAuth]

每個站點都需要一個用戶名密碼,於是你會有一堆的用戶名密碼需要保管,因此從安全的角度來說你需要一個可靠的密碼管理器。

但是!但是另外一種做法是你可以減少注冊的用戶名和密碼,通過減少用戶名密碼的數量來解決用戶的痛點,更重要的是在安全的基礎上帶來更強的能力。

如果我們只要注冊一個公共的賬號(用戶名/密碼),讓其他網站都能做到下面兩個目的:

  1. 身份驗證(Identity, authenticate):不需要知道用戶的密碼就可以驗證用戶的身份,從而提供登錄服務。
  2. 授權(authorization):網站A的用戶能夠在不提供密碼給網站B的情況下,安全地授權給網站B在限定時間內去訪問用戶在網站A上指定資源的權限。

可以看到authenticate和authorization,很接近的兩個單詞,代表了兩件不同的事:A授權給B使用某個資源,但是網站A並不能驗證當前訪問資源的是A還是B。

OpenID和OAuth正是解決這個問題的兩個標准協議,但是他們各自解決的問題不同,我們先梳理下它們之間的關系。

  1. OAuth解決的是如何授權的問題。OAuth經歷了OAuth 1.0和 OAuth2.0。
  2. OpenID解決的是如何驗證身份的問題。OpenID經歷了OpenID1.0,OpenID2.0,最新的標准是OpenID Connect(OIDC)。 OpenID 1.0和OpenID 2.0是和OAuth獨立的體系,但是OpenID Connect是以OAuth 2.0為基礎的協議,可以看作是OAuth的一個應用。

因此,總的來說只要理解了OAuth 2.0的基本原理,就可以理解OpenID Connect。那么就理解了“授權”和“身份驗證”兩件事最新的是怎么做的即可。

現在,理解下OAuth 2.0的基本做法,先定義幾個關鍵角色:

  1. 資源持有者(Resource Owner)
  2. 資源服務器(Resource Server)
  3. 終端用戶(Client)
  4. 授權服務器(Authorization Server)

我們來看Client是如何獲得Resource Owner授權,從而在有效期內使用Resource Server上特定資源的:

  1. Alice在網站R上有一堆資源。
  2. Bob想要在網站S上使用Alice在網站R上的資源。
  3. Bob向Alice請求獲得授權。
  4. Alice使用用戶名密碼登陸網站R。
  5. 網站R返回給Alice一個授權碼(Authentication Code)。
  6. Alice向Bob返回授權碼。
  7. Bob使用授權碼向網站R請求獲得授權令牌(Access Token)。
  8. 網站R返回給Bob返回授權令牌。
  9. Bob發送授權令牌給網站S。
  10. 網站S使用Access Token在有效期內訪問Alice在R上的特定資源。

基本過程就是這樣的,Bob在這里未必是指另一個人,而可能是一台機器,一個瀏覽器,一個客戶端。OAuth 2.0上解決不同的安全需求也會在協議步驟上有不同的變種。核心原理就是:

  1. Bob必須通過只有Alice持有的用戶名和密碼授權獲得一個AccessToken。
  2. 這個AccessToken有過期時間和能訪問的特定資源限制。

而前面說了OAuth 2.0只規定了如何授權。通過授權可以做的事情很多,包括在授權的基礎上實現身份認證。如果每個OAuth 2.0的使用者可以自己在OAuth 2.0的基礎上做這個事情。另一方面,OpenID 1.0和OpenID 2.0雖然目的是身份驗證,但是協議本身的過程和OAuth多少有些冗余重復。OpenID Connect就是直接對使用OAuth 2.0做身份認證提供了標准。

OpenID Connect的基本過程如下:

  1. 終端用戶(End User, EU)需要在網站S上登錄,網站S叫做依賴方(Relying Party,RP)。
  2. 網站S向終端用戶的開放身份認證提供商(OpenID Provider, OP)請求身份認證。
  3. 開放身份認證提供商對終端用戶進行身份認證,例如要求終端用戶輸入OpneID的用戶名密碼等。
  4. 開放身份認證提供商返回身份認證令牌(IDToken)給網站S,以及一個授權令牌(AccessToken)。
  5. 網站S使用AccessToken向開放身份認證提供商發送一個請求,以獲得終端用戶的UserInfo。
  6. 這樣網站S就完成了對終端用戶的身份認證,獲得了其<IDToken, UserInfo>信息。

小結:

  1. OAuth2.0完成授權,OpenID Connect基於OAuth2.0完成身份認證。
  2. 關鍵字:AccessToken,IDToken。

[1] wiki:OpenID
[2] OAuth
[3] OAuth 2.0
[4] The OAuth 2.0 Authorization Framework
[5] The OAuth 2.0 Authorization Framework: Bearer Token Usage
[6] OpenID Connect

0x06 [IDToken] vs [JWT, JWS, JWE]

在OpenConnect里面,IDToken是身份認證傳遞的最重要的數據。那么一個IDToken的結構是怎樣的呢?

在OpenConnect里面,IDToken采用了JSON Web Token (JWT)格式,這個格式大概如下的JSON格式:

{
 "iss": "http://server.example.com",
 "sub": "248289761001",
 "aud": "s6BhdRkqt3",
 "nonce": "n-0S6_WzA2Mj",
 "exp": 1311281970,
 "iat": 1311280970,
 "name": "Jane Doe",
 "given_name": "Jane",
 "family_name": "Doe",
 "gender": "female",
 "birthdate": "0000-10-31",
 "email": "janedoe@example.com",
 "picture": "http://example.com/janedoe/me.jpg"
}

有了JWT,從安全上來說,對JWT有兩種操作需求:

  1. 簽名
  2. 加密

首先,簽名的結構包含3部分:Head.Payload.Signature

  1. Head: {"kid":"...", "alg":"..."},alg表示簽名使用的加密算法,還有很多其他可選字段
  2. Payload: 就是上面的JWT的
  3. Signature: 使用加密算法對Payload簽名

然后把這三分部通過下面的公式用點號拼接在一起:
BASE64URL(UTF8(Head)).BASE64URL(Payload)).BASE64URL(Signature).

所以解碼的時候,只要先用Base64解碼,再對簽名做校驗即可。

其次,加密的結構包含5部分: Head.Key.Vector.Payload.Tag,基本做法如下的兩步加密:

  1. 使用對稱加密算法Key和初始化向量Vector,對Payload加密
  2. 使用非對稱加密算法對Key加密。

所以這5部分實際上分別是:

  1. Head: {"alg":"...", "enc":"..."}。enc是對稱加密算法,alg是非對稱加密算法。
  2. Encrypted Key,使用非對稱加密算法alg對Key加密
  3. Initialization Vector,對稱加密算法的初始化向量
  4. CipherText,使用Key+Initialization Vector對Payload加密
  5. Authentication Tag,校驗碼

然后把這五部分通過下面的公式用點號拼接在一起:
BASE64URL(UTF8(Head)).BASE64URL(EncryptedKey).BASE64URL(InitializaionVector).BASE64URL(CipherText).BASE64URL(AuthenticationTag)

最后,如果你想同時對JWT簽名+加密,可以這么做:

  1. 使用JWS對JWT簽名
  2. 把步驟1的簽名數據作為JWE的Payload

小結一下,IDToken,JWT、JWS、JWE的關系是:

  1. JWT是IDToken的一種。
  2. JWT可以使用JWS簽名。
  3. JWT可以使用JWE加密。
  4. JWT可以同時使用JWS+JWE簽名和加密。

最后說一句,身份驗證和授權機制,在封閉的內部系統里都可以自己做一套,標准化的目的是提供不同服務商之間的互通性,減少巴別塔。理解基本原理可以自由構建,但是理解標准則是另一個目標。

[1] Understanding IDToken
[2] jwt.io
[3] rfc-7519: JSON Web Token (JWT)
[4] rfc-7515: JSON Web Signature (JWS)
[5] rfc-7516: JSON Web Encryption (JWE)

0x07 如何生成強密碼

// TODO

0x08 OPenGPG

A和B通信

  1. A生成一個隨機數R,用B的公鑰加密這個隨機數R,得到S
  2. A用R作為對稱加密算法的密鑰,加密信息M,得到E
  3. A把(S,E)發給B
  4. B用自己的私鑰解密S,得到R
  5. B用R作為對稱加密算法的密鑰,解密E,得到M

所以最關鍵的是A怎樣確認B的公鑰確實是B的。在公共鑰匙設施(Publick Key Infrastructure)的情況下,你需要有一個證書頒發和驗證中心,但是這個中心機構是會作惡的。

OpenGPG走的就是【朋友圈認證】機制,只要A的N個可信的朋友圈認證一個公鑰確實是B的,A就認可B的這個公鑰。同時A也加入了B的可信朋友圈,可以為B的公鑰背書。

[1] https://tools.ietf.org/html/rfc4880

--end--


免責聲明!

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



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