認證系統幾種方式


認證系統幾種方式

https://zhuanlan.zhihu.com/p/50763432

 

認證(authentication)和授權(authorization)是經常一起出現的詞匯,它倆用途截然不同,認證解決的是如何確定用戶的身份,授權是在認證之后,知道用戶身份的前提下,決定一個用戶擁有什么權限。

認證系統包含兩個方面:密碼存儲方案和認證協議,密碼如何存儲會限制可供選擇的認證協議。

1 密碼存儲方案

很多人都傾向於使用相同或者類似的密碼登錄不同服務,因此密碼存儲方案的核心需求是原始明文密碼不能被容易的恢復出來,以免殃及其它服務。可惜實際上不同服務由不同方控制,同一密碼用不同存儲方案保存,安全性取決於最弱的那個。好的服務采用 BCrypt,架不住糟糕的服務用明文保存同一個密碼,所以還是需要用戶做到一站一密,或者服務自己做多因子認證,至於采用安全性好的密碼存儲方案,這是對一個認證系統的基本要求。

實際工程實現中,密碼如何存儲經歷很多變遷,可惜的是業界並不總是能吸取教訓的,2002 年發布的 Mac OS X 10.2 使用的密碼加密算法跟 1979 年 Unix第七版的算法是一樣的,而且沒有采用類似 /etc/shadow 的機制,這個機制是 UNIX 在 1987 年引入的,到了 Mac OS X 10.3 的時候,Apple 更改了算法,引入了 shadow 機制,但卻沒有使用 salt。大公司尚且如此馬虎,何況互聯網界多如牛毛的中小公司了,時至今日,肯定有一小撮公司使用明文存儲密碼,一大撮公司用通用摘要算法或者不用 salt,肯定有好大一批公司把用戶詳細信息跟密碼存儲在一起,web server 運行的賬戶 www 或者 nobody能讀取所有密碼。

理想的做法是把用戶信息(用戶名到手機號、郵箱、性別等的映射)和認證信息(用戶名到密碼的映射) 分開存放到不同系統里,尤其是跟業務系統分開,因為業務系統涉及到繁雜的業務邏輯,出問題的概率最大。

Salt 的作用是避免詞典攻擊,所謂詞典就是預先算好的摘要值到密碼的映射表,大家的密碼一般比較短,所以這個表文件可以不大但覆蓋巨量密碼,有了 salt 后,這個映射就成了 password + salt -> hash,變相的提升了密碼長度,只要不同認證系統的 salt 不一樣,就沒法查表反推原始密碼了。看起來有了 salt 后就相當安全了,但其實沒有解決暴力破解問題,如果摘要算法是MD5並且密碼長度只有六個字符時,暴力破解一個密碼只需要一分鍾,  ,這就是為什么會有人設計專門針對密碼的哈希算法。

1.1 明文

存儲明文密碼對認證協議的限制是最寬泛的,但此種方案顯然是無法滿足上面說的核心需求。

1.2 摘要值

早期 UNIX 限制密碼長度,采用加密算法如 DES 對原始密碼加密保存,現在的認證系統都使用摘要算法,對密碼以及一個隨機串(稱為 salt)應用一個摘要算法,比如 MD5, SHA1, BCrypt,然后保存摘要值以及salt。這種方式是最廣泛采用的,但在選擇摘要算法時要避免通用摘要算法,因為他們的設計考慮了要快速運算,不利於提高暴力破解的難度,應該選用特別針對密碼設計的摘要算法,這類算法被稱為key derivation function(KDF),其原理都是通過可配置的迭代次數調節計算量,比通用摘要算法慢幾個數量級。

更寬泛點說,依靠生物信息識別用戶,也可以認為是把生物信息做了個摘要保存起來,比如指紋,提取指紋特征很容易,但依據這些特征反推指紋出來是很難的,而暴力破解就更難了,單次驗證的時間已經在秒甚至分鍾級別,只能以偷摸或者暴力的手段直接獲取“生物體”本身或其克隆了,這在電影里很常見:-D

下面的三種 KDF,理論上安全強度 SCrypt > BCrypt > PBKDF2,推薦使用 BCrypt。

1.2.1 PBKDF2

Password-Based Key Derivation Function 2, 由 RSA 實驗室提出。這個算法各個主流語言都有實現(),應用也非常廣泛,比如 WPA/WPA2, WinZip, LastPass, Mac OS X, iOS, Android, Django, Zend, GRUB 2。

PBKDF2 的缺陷是容易被特制的芯片破解,比如使用 ASIC 或者 GPU,所需的電路和 RAM 都很小。

1.2.2 BCrypt

BCrypt 基於 Blowfish 加密算法設計,Blowfish 是密碼學界泰山北斗級人物 Bruce Schneier 的大作。跟所有的 KDF 一樣,Bcrypt 也是通過調整迭代次數降低運算速度。OpenBSD 率先采用 BCrypt 保存 /etc/shadow 里的密碼, PHP 5.5 的 password_hash 函數已經把 BCrypt 作為默認算法,互聯網界各路專家推薦,各家公司采用,儼然已經成為密碼摘要存儲的標准方案。 

BCrypt 被特制芯片破解的難度稍微大點,所需的電路和 RAM 比 PBKDF2 要多些,但依然是固定的。

1.2.3 SCrypt

SCrypt 特意增大了運算所需內存,因此提高了特制芯片破解的成本,此算法被萊特幣(Litecoin) 所使用,但似乎在互聯網界並不廣泛,可能是大家剛轉向 BCrypt,沒工夫搭理尚顯年青(2012 年被提出)的SCrypt,或者是采用 SCrypt的性價比對互聯網企業不合算。

1.3 一次一密算法的參數

在 OTP(One-time password) 算法中,需要在服務端和客戶端儲存一些參數或者狀態,隨后認證過程中雙方才能使用同樣的秘鑰函數計算出當前秘鑰。

1.4 客戶端密碼存儲

頻繁輸入密碼會讓用戶很惱火,有兩種辦法解決:在客戶端存儲一個 cookie,標記其登錄過,並設置過期時間;讓客戶端保存密碼並自動輸入。Web 瀏覽器是典型的保存密碼並自動輸入的例子,可惜的是伊默認不強制要求設置 master password 以保護那些保存的密碼,在設置對話框里就能看到明文密碼。。。。

多說兩句 cookie。現如今絕大多數網站在用 https 登錄后會傳給瀏覽器一個 cookie,記錄其 session id 什么的,然后轉用 http 協議。這是個非常滑稽的事情,知道 Firesheep() 的教訓的人依然非常少,很少人意識到這是個巨大的安全漏洞。在有線局域網內,處於同一個 VLAN 的用戶是可以抓到這個 VLAN 里的所有數據包的。在采用 WEP 認證的無線網里所有人共用一個秘鑰加密網絡流量,每個登錄成功的人可以破解所有網絡流量。在 WPA/WPA2 認證的網絡里,雖然每個用戶的會話秘鑰各不相同,但在 WPA-PSK/WPA2-PSK 模式里攻擊者可以比較容易的通過 reauth 攻擊得到別人的會話秘鑰,被攻擊者一般毫不知情(掉線了會自動重連),只有 WPA-Enterprise/WPA2-Enterprise 模式(在無線路由管理界面上一般標記為 WPA/WPA2)沒有這個問題,但在家庭網絡、咖啡館、麥當勞這些地方全都用的 PSK 模式,只要一台機器搞鬼或者中木馬,整個網絡都不安全。一旦竊取到 cookie,那就完全可以扮作被害人操作網站,由於這種攻擊是在同一個局域網內,公網 IP 是一樣的,服務端也很難區分出來。竊聽流量獲取 cookie 還是一種被動攻擊,如果主動攻擊 http 流量,插入或者替換 JS 腳本,被攻擊者上當了都難以發覺。總之,在用 WEP、WPA-PSK/WPA2-PSK 認證的地方使用重要服務時,最好保持 https 或者使用 VPN。Firefox 有兩個插件可以強制對某些域名使用 https:

可惜,全面默認 https 的風氣還沒興盛起來,有的大網站居然都沒處理好https,比如訪問  和  就會發現伊們的 X509 證書是簽發給 s.tbcdn.cn的,Web 瀏覽器發現域名不匹配就會警告,可憐這還是從 VeriSign 購買的證書,你們沒錢買足夠的證書就從  申請個免費的嘛。。。。

BTW,看 Firesheep 介紹時順帶了解了下 WPA 的安全性,居然發現 WPS 有重大漏洞,無語了:  各位看客趕緊檢查自己的無線路由。。。。

也有專門的軟件保存密碼,有些還能自動輸入密碼到其它軟件。LastPass() 應該是最有名的了,但把密碼放在它的服務器上總是有點不大放心,伊也曾爆過安全丑聞。類似功能的開源替代品很多,密碼被加密后放在本地,雖然遷移不方便,但終歸是蛋捏自己手里:

  • KeePass: http://keepass.info ,1.x 版只能用於 Windows 系統, 2.x 版本使用 Mono 編寫,支持 Windows/Linux/MacOS X,手機上也有它的各種移植、兼容版本:
  • KeeFox:  ,Firefox擴展,連接 Firefox 和 KeePass
  • KeePassX: , KeePass 1.x 的 Linux 移植版,使用 Qt 庫編寫,支持 Windows/Linux/MacOS X
  • KWallet: , KDE 桌面環境的密碼管理器
  • Seahorse: , Gnome 桌面環境的密碼管理器
  • Mac OS X keychain: Mac OS X 自帶的密碼管理器
  • VIM:VIM 編輯器的 -x 選項可以用來創建加密文件,一旦加密后下次 VIM 打開時會自動識別出來是加密的文件並詢問密碼。使用這個特性時記得設置 encryptmethod 選項為 blowfish。我一直用這個,查找、編輯都很順手,VIM 做文本操作就是快捷!

2 認證協議

用戶注冊時選擇密碼並被服務端保存,隨后用戶訪問這個服務時就要經過認證協議。密碼如何存儲是服務內部的問題,除非服務被攻破,存儲的密碼泄露,否則還是不大容易捅簍子的,而認證協議解決的是在網絡兩頭雙方的信息交換,這就太容易出問題了,所以出現繁多的認證協議也就不足為怪。

2.1 雙方認證和三方認證

從認證涉及的各方來看,認證協議分為雙方認證協議,客戶端和服務端,這是最常見的,還有三方認證協議,除了客戶端和服務端還引入了一個雙方都信任的第三方,比如 Kerberos, CAS(Central Authentication Service), OpenID。下面只看雙方認證協議,這也是三方認證的基礎。

2.2 單因子認證和多因子認證

雙方認證按照認證時提供的憑證個數分為單因子認證(SFA, single-factor authentication)和多因子認證(MFA, multi-factor authentication),而多因子認證里以雙因子認證最為常見。

所謂多因子,指認證時需要提供如下憑證:

  1. knowledge factor (something only the user knows),比如密碼,某個秘密問題的答案,某個特定模式比如Android上鎖屏解鎖時的特定滑動序列。
  2. possession factor (something only the user has),比如銀行卡, RSA SecurID, 各種銀行提供的電子令牌,YubiKey,以及人手一部的手機,各種認證系統往手機發送認證碼就是假定了持有手機的人是目標用戶。
  3. inherence factor (something only the user is),比如指紋,視網膜,語音。

也有人提第四個因子,somebody you know。

單因子認證一般只需要提供第一個因子(也有只需要第二個因子的,譬如通過目測護照識別身份),雙因子一般是提供第一個和第二個因子。多因子認證並不是絕對安全,只是增加被破解的難度。注意有一些認證系統看起來像是雙因子認證,其實是單因子認證,比如登錄時除了密碼,還要回答一個秘密問題,或者可以通過緊急郵箱找回密碼,這種做法通過讓用戶提供多個knowledge factors 提高破解難度。

在網上登錄絕大部分情況下都是單因子認證,一個密碼了事。使用網上銀行或者 ATM 機是典型的雙因子認證,除了密碼之外還要提供電子令牌上的數字或者銀行卡,雙因子認證方案里幾乎不可能被完全不相關的黑客拿到兩個因子。

雙因子認證很容易在實現時引入漏洞:丟失密碼后使用手機發短信即可重置密碼(要求額外提供身份證號是不保險的,身份證號並不是保密信息),或者手機上的應用長期緩存第一個因子,只需要提供第二個因子。在安全和方便之間總是難以皆大歡喜。

2.3 互相認證 (mutual authentication)

關於認證還有另外一個問題,大家都明白客戶端需要向服務端證明自己身份,很多人可能忽視了服務端向客戶端證明自己身份的必要性,這點一旦點出,大家自然明白,好比影視里倆同志接頭要互報口令。這也是為什么稍微正經的認證系統登錄時需要用 SSL/TLS 的原因之一(另一個重要原因是加密通訊避免用戶密碼在傳輸時被竊聽),利用 SSL/TLS 客戶端檢查服務端的 X509 證書。

2.4 協議

具體的認證協議五花八門,可以一刀切分為兩類:需要向對方發送密碼的,不管是固定密碼還是一次一密;不需要向對方發送密碼的。

2.4.1 需要向對方發送密碼的認證協議

這種協議都需要 SSL/TLS 之類的協議護駕,否則毫無安全可言。

2.4.1.1 HTTP Basic, SMTP PLAIN, SMTP LOGIN

把用戶名、明文密碼或者BASE64編碼的密碼發送給服務端。這個是最容易實現的,在 HTML 登錄表單里也很好做,服務端可以拿密碼跟明文密碼比對(不推薦),或者拿提供的密碼與 salt 做摘要再跟期望的摘要值比較。

2.4.2 不需要向對方發送密碼的認證協議

2.4.2.1 CRAM-MD5

Challenge Response Authentication Mechansim, 所謂的 challenge 就是服務端發給客戶端一個隨機字符串,客戶端需要用密碼或者密碼的摘要值對其進行 HMAC-MD5 運算,然后把結果發送給服務端,服務端對隨機串做相同運算並比對結果。

此協議只是客戶端向服務端認證,沒有服務端向客戶端認證,因此一般需要 SSL/TLS 護駕,讓客戶端驗證服務端證書。具體實現時那個 challenge 往往有比較固定的模式,沒有 SSL/TLS 信道加密的話,通訊數據包被竊聽后易受詞典攻擊。

在服務端,密碼要么是存為明文,要么是存為 MD5 摘要值或者中間運算結果,一是容易被暴力破解,二是存儲的值在 CRAM-MD5 認證協議里跟密碼等價,所以拿到這個摘要值其實就是獲得了此用戶的權限。

2.4.2.2 DIGEST-MD5

相比 CRAM-MD5,在認證過程中允許客戶端提供一個隨機串添加在服務器給定的隨機串上,因此避免了惡意的服務端做選擇明文攻擊(CRAM-MD5 中對選定明文,客戶端返回的摘要值是確定的,因此可以被詞典攻擊)。

DIGEST-MD5 支持互相認證,但協議本身選項比較多,容易實現不當,互操作性比較差。

雖然攻擊難度比 CRAM-MD5 大,但一般也需要用 SSL/TLS 保護信道以免竊聽。

跟 CRAM-MD5 一樣,密碼是 MD5 摘要,而且在認證協議里等價於密碼,因此在服務端存儲的密碼是相當不安全的。

2.4.2.3 SCRAM (Salted Challenge Response Authentication Mechanism)

SCRAM 是一族算法,最常見的是 SCRAM-SHA1,設計用來替換 DIGEST-MD5。 SCRAM 比 DIGEST-MD5 更安全也更容易實現,XMPP 把 SCRAM 列為必須支持的認證協議。雖然 SCRAM 規定 SHA1 為必須支持的摘要算法,但 SCRAM 並不限制摘要算法,可以使用 IANA 規定的任何算法:

SCRAM 認證是互相認證。存儲在服務端的 StoredKey 是 PBKDF2 摘要算法的結果的再次摘要,在 SCRAM 中不是 password 的等價物,即使泄露也不能偽裝用戶,但存儲在服務端的 ServerKey 一旦泄露,攻擊者可以偽裝服務端。 

SCRAM 需要搭配 channel binding 以避免中間人攻擊,可以用 SSHv2 和 TLS。所謂通道綁定就是應用層的認證協議利用傳輸層的加密協議,確認在應用層認證的雙方確實是互相通信的雙方,避免中間人攻擊,注意這里的通道綁定是需要兩層協議的具體實現互相支持的,比如上層協議要獲取 SSHv2 的 session ID 或者 TLS 里的握手報文內容(tls-unique binding)、X509 證書 (tls-server-end-point binding) 參與認證過程,舉例來說,在 TLS 上做通道綁定的 SCRAM-SHA-1 增強版叫 SCRAM-SHA-1-PLUS, 其實現需要 OpenSSL 或者 GnuTLS 庫提供獲取握手報文內容、X509 證書的 API。

2.4.2.4 SRP (Secure Remote Password protocol)

與 Kerberos 和 SSL X509 不同,SRP 並不依賴第三方的受信秘鑰服務或者證書分發機構,SRP 使用共享密碼做互相認證。SRP 有大量優良特性:

  • 容許弱密碼,攻擊者必需跟服務端或者用戶端交互才能暴力破解
  • 服務端存儲的 salted password 不是密碼的等價物,而是類似於公鑰;
  • 認證過程不需要傳輸層加密
  • 認證過程可以生成一個會話秘鑰

參考:

OpenSSL >= 1.0.1 以及 Apache 2.5 mod_ssl, mod_gnutls 支持 TLS-SRP:

但是很不幸 Redhat 為了避免可能的專利糾紛刪除了 Fedora、RHEL 中 openssl 軟件包里的 srp 代碼:

2.4.2.5 AKA

Authentication and Key Agreement,用於 3G 網絡中,提供互相認證以及加密通道。

2.4.2.6 EAP

Extensible Authentication Protocol,EAP 是一個認證框架,常用於無線網以及點對點網絡中。具體的認證方法稱為 EAP method,目前定義了大約四十種。

EAP-TLS: 使用 client & server X509 certificates互相認證,並用TLS加密信道

EAP-POTP: 使用 OTP token 做雙因子認證

EAP-PSK: 使用 pre-shared key 做互相認證,認證成功后信道被加密

EAP-PWD: 從一系列共享密碼中挑選一個做認證,被 Android 4.0, FreeRADIUS, Radiator 支持

EAP-IKEv2

EAP-FAST

EAP-AKA

PEAP: 為 EAP 提供加密保護

2.4.2.7 RADIUS

基於 UDP 協議。在使用 WPA-Enterprise/WPA2-Enterprise 無線網認證方式的地方就需要 RADIUS 服務。

2.4.2.8 TACACS+

Cisco 開發,基於 TCP 協議,提供 authentication/authorization/accounting.

2.4.2.9 Diameter

代替 RADIUS,提供 authentication, authoriazation, accounting。

2.4.3 雙因子認證

以前雙因子認證還是個高級貨,只用在網上銀行以及大型企業 IT 系統的登錄系統里,在 Google 推出雙因子認證后, Internet 巨頭們紛紛支持,加上智能機普及,雙因子認證算是飛入尋常百姓家了。

上面提到 SRP、SCRAM,看起來是很安全了,但是總架不住客戶端中了木馬導致密碼泄露,或者密碼比較二被人猜出來,或者一個密碼打天下忽然驚聞常去的某網站居然是明文存儲密碼,等等等等,所以牽涉到用戶深度隱私或者錢財的服務必須自覺的支持雙因子認證。

一般雙因子認證使用這兩個因子: knowledge factor,基本都是指密碼了, possession factor,電子令牌上或者手機上的 Google authenticator 應用顯示的認證碼,或者是服務端通過短信發到手機上的認證碼,這個認證碼就是個 one-time password,其生成算法是有業界標准的,並不是個簡單的隨機數。

Wikipedia 上對 OTP 的講解很清楚:

簡單來說,TOTP 就是雙方共享一個種子,用一個函數對這個種子以及時間原點到當前逝去的分鍾數或者 30s 數目求值,由於種子和時間雙方都一致,所以自然解決了認證碼的過期問題,以及跟用戶對應的問題。這個算法需要客戶端和服務端的時間偏移不能太大,TOTP 的 RFC 提到如何容忍稍許的時間不同步,根據用戶的輸入記錄時鍾偏移,比如用戶連續三次輸入上一分鍾的認證碼,那么服務端就知道用戶的時鍾慢了一分鍾。

HOTP 是雙方定一個種子數字,用同一個摘要函數這個種子求值,對結果再次算摘要值,如此反復,由於摘要函數的特性,很難從下一個值推算出上一個值,所以把這些值倒過來就是一個密碼表了,每次用下一個密碼。

TOTP 比 HOTP 用的更廣泛,因為 HOTP 每次使用時都有一個當前狀態需要記錄,使用上不大方便,而 TOTP 只需要種子數字以及時間同步,另一個 HOTP 的問題是一旦用戶不小心泄露了后面的密碼,那么這個密碼之前的密碼都泄露了,通過對后面的密碼做摘要即可得到前面的密碼。

明白原理后就很容易理解 OTP 是怎么用的了:

  • 起始種子的生成
    • 對硬件形式的電子令牌,生產時會設置好種子到硬件里並備案,管理員購買后會把種子數字輸入到 OTP server 里。
    • 對手機上的 OTP 軟件,服務端可以發短信、語音到用戶,或者網站上生成種子數字的 QR code,用戶拿手機掃描出來,然后這個種子被輸入到 OTP 軟件里;
    • 服務端也可以不把種子發給用戶,只是讓用戶注冊手機號,每次用戶要登錄時,用戶點擊網頁上的獲取驗證碼按鈕,服務端就會把當前的認證碼通過短信發送到用戶的手機上。這種方式安全點,不用擔心用戶方泄漏了種子數字;
    • 在生成種子的時候,服務端還可以生成一系列的 backup codes,這些數字等價於認證碼,用完一個即作廢一個。backup codes 是需要用戶保存好的,比如打印出來,backup codes 的目的是用戶在丟失手機后可以用 backup code 登錄。

 

  • 認證碼的使用:電子令牌或者手機上的 OTP 軟件會顯示當前的認證碼,用戶在登錄服務時需要輸入用戶名、密碼、認證碼。注意得到認證碼的過程是本地算出來的,不需要聯系服務器。

Google 的認證系統還有個高級功能,可以為一個賬戶生成多個副密碼,這些密碼不需要雙因子認證,這個功能是為了給第三方不支持雙因子認證的應用訪問 Google 服務。

雙因子認證提高了認證系統的安全性,但並不意味着認證過程絕對安全,一旦用戶密碼和某次認證碼泄露(比如通過鍵盤鈎子記錄按鍵,然后切斷用戶和服務端連接),攻擊者可以立馬登錄然后修改密碼重新綁定手機重新生成種子,當然這么搞會被用戶發覺。也可以隱蔽點,如果攻擊者和用戶在同一個內網里,攻擊者和用戶先后登錄,並且Web瀏覽器指紋一樣,服務端是沒法區分的。

Google authenticator 官方自稱 twp-step authentication 而非 two-factor authentication,因為有人詬病它的安全性。傳統意義上的 possession factor 是很難復制的,要么擁有要么沒有,比如 RSA SecurID 就是抗篡改的(tamper-resistant ),而 Google authenticator 可以同時在多個設備上運行,只要把種子數字從手機里復制出來,這破壞了“something only the user has”的要求。但總之這種 soft token 還是聊勝於無,窮人的福利。

2.4.4 CAPTCHA

CAPTCHA 是 Completely Automated Public Turing test to tell Computers and Humans Apart 的縮寫。為了避免腳本自動注冊、登錄,在注冊或者登錄表單里添加一個輸入框以及小圖片,圖片上顯示一些扭曲的文字,需要用戶肉眼識別出來並填入那個輸入框里。有的 CAPTCHA 也提供語音輸出。

原理很簡單,實際應用中也很常見,但做好並不容易,需要挖空心思讓機器圖形識別困難,但對人肉識別又比較容易,看起來很凌亂的圖片,未必難於被機器識別。

順帶八卦一下,某些網站的 CAPTCHA 做成了一個廣告圖片,要求輸入廣告里某個字眼,做法相當高明,用戶不得不看廣告。

3 無責任推薦

依優先級順序,排在前面的優先級高。

3.1 Kerberos/SPNEGO, OpenID

Intranet 使用 Kerberos 和 SPNEGO 做 single sign-on,這個選擇已然定論,支持這些協議的操作系統和應用軟件都非常廣泛。

Internet 使用 OpenID,不用自己操心認證的事情了。但用 OpenID 也是有些煩人的因素了,自己網站的用戶登錄狀況被第三方知道多少是有點讓人不爽的事情,蛋捏別人手里。對認證的安全把握也完全沒有,我就從來不敢在手機上的非 Google 應用里輸入 GMail 賬號信息,鬼知道密碼是送給李逵還是李鬼了,界面上也沒有什么 OpenID seal 可供識別,在瀏覽器上還稍微放心點,畢竟瀏覽器是個相對安全的中立方(排除個別別有用心的瀏覽器)。

估計大伙也是這么想的,所以雖然 OpenID 想法很好,但大家都把它當做錦上添花的特性,不會作為主要的認證方式。

3.2 TLS X509 server certificate + SRP or TLS-SRP

SRP 用於認證本身並不需要 SSL/TLS 保護,但實際應用中需要登錄往往意味着需要加密隨后的通信,TLS-SRP 是 TLS 協議對 SRP 的直接支持。在 TLS 協議中涉及到四類密碼學算法:

  • 認證:互相識別對方身份,可以用 X509 證書(涉及 RSA or DSA), PSK(pre-shared key), SRP,其中 SRP 可以搭配 RSA、DSA 混用增強安全性;
  • 秘鑰交換:在不可信通道上交換一個共享的對稱加密秘鑰,用於隨后加密連接,可以用 RSA, SRP, DH, ECDH。
  • 加密算法:加密連接,可以用 3DES, AES 等;
  • 摘要算法:在認證和秘鑰交換過程會頻繁用到摘要算法,比如 MD5, SHA1;

可以看出 SSL/TLS 協議並不一定需要 x509 證書,可惜所有 Web 瀏覽器只支持 X509 證書方式的認證,並且對客戶端的 x509 證書認證操作比較麻煩,需要用戶自己在瀏覽器設置里導入客戶端自己的證書,所以為了應付 Web 瀏覽器,還是需要結合 TLS X509 server cert 做服務端認證然后加密連接,然后再用 HTML 表單以及 JavaScript 做 SRP 認證(這一步不依賴加密連接),如果是本地應用,可以直接上 TLS-SRP。

TLS-SRP 要求 OpenSSL >= 1.0.1 或者 GnuTLS。 OpenSSL 1.0.1 在 2012 年 3 月 14 日發布。

SRP 在服務端保存的是 verifier,而非 password 或者 password 的等價物, verifier 類似公鑰認證里的公鑰,所以泄露了也太大問題。

3.3 TLS + SCRAM

使用帶有 TLS channel binding 的 SCRAM-SHA-1-PLUS。

3.4 TLS X509 server certificate + PLAIN

實現簡單,理解容易,業界最廣泛使用的方案。用 x509 證書驗證服務端,然后在加密連接上傳輸密碼或者其摘要值給服務端以驗證客戶端。密碼存儲使用 BCrypt。

使用 TLS 的注意事項:

  • 使用 OpenSSL >= 1.0.0 的 ECDHE 特性獲得 perfect forward secrecy 並且保持高性能: 
  • 對 native client 使用 TLS >= 1.1 : 
  • 對瀏覽器使用 SSL >= 3.0 (包含了 TLS 1.0,雖然有問題但是老版瀏覽器不支持 TLS >= 1.1)。
  • 禁用 TLS compression 和 HTTP compression: 
  • TLS 秘鑰交換算法選擇優先次序:ECDH > DH > RSA(不支持 perfect forward secrecy) > ECDSA(不是所有客戶端都支持)。
  • 讓 server 端決定 cipher 優先級順序(Apache: SSLHonorCipherOrder On, Apache TrafficServer: proxy.config.ssl.server.honor_cipher_order 1),而非默認的讓 client 決定。
  • 禁止 TLS client 發起的 renegotiation,對於長連接,server 應在一小時之內發起 renegotiation,短連接應禁止 renegotiation。
  • Abbreviated handshake (session ID or session ticket extension)有效時間不超過兩小時。
  • 優先使用 AES 加密算法,以利用 AESNI 硬件加速。

如果服務端是一個集群,那么 TLS session ID 需要搭配 memcached 做共享的 session cache,而 session ticket extension 需要集群所有機器使用同樣的ticket key。

這個方案有兩個問題,TLS 證書驗證由於用戶可能盲目信任未知證書而導致中間人攻擊;有可能失誤會導致密碼被明文傳輸,譬如客戶端邏輯出錯沒有啟用 TLS 連接,譬如登錄頁面沒配置成 https only。

4 實現參考

認證協議是個理解起來傷腦筋,要想實現無誤也很費神的事情,有人就構建了許多框架或者 API 來容納各種認證協議:GSSAPI(Generic Security Services Application Programming Interface),SASL(Simple Authentication and Security Layer), SSPI(Security Support Provider Interface),其中應用最廣的當屬 SASL,眾多網絡協議以及 Linux 下無數應用都支持 SASL,不過最遺憾的是 HTTP 協議以及眾多 web 瀏覽器不支持它。

SASL 主流實現有四個:

這些 SASL 實現可以從文件、OpenLDAP、關系數據庫讀取密碼信息並進行驗證,也能更改密碼,列舉用戶名,在實現認證系統時最好基於某個 SASL 實現。

4.1 TLS X509 server certificate + SRP or TLS-SRP

單純的 SRP 實現在 wikipedia 頁面上列了很多,目前只有 Cyrus-SASL 支持 SRP。 TLS-SRP 需要 OpenSSL >= 1.0.1 或者 GnuTLS 支持。注意 Fedora、RHEL 官方的 openssl、gnutls 軟件包剔除了 SRP 相關代碼以避免潛在的專利糾紛。

目前沒有 Web 瀏覽器直接支持 SRP,需要先用 TLS x509 server cert 建立 https 連接,然后用 HTML 表單以及 JavaScript 做 SRP 認證。雖然這個 x509 server cert 不用於認證,但還是需要正規 CA 簽發的證書,以免中間人攻擊替換掉 HTML 表單以及 JavaScript 從而導致明文密碼泄露。

非 Web 瀏覽器場合,可以直接上 TLS-SRP,不需要 X509 證書。

下面是分別用 OpenSSL 和 GnuTLS 演示 TLS-SRP。

4.1.1 OpenSSL

$ touch srpvfile.txt
$ openssl srp -srpvfile srpvfile.txt -userinfo "my test user" -add testuser
$ openssl s_server -nocert -cipher SRP -srpvfile srpvfile.txt -accept 4430
$ openssl s_client -srpuser testuser -cipher SRP -connect localhost:4430

4.1.2 GnuTLS

$ srptool --create-conf srppasswd.conf
$ srptool --passwd-conf srppasswd.conf --passwd srppasswd.txt -u testuser
$ gnutls-serv -p 4430 --http --srppasswdconf srppasswd.conf --srppasswd srppasswd.txt --priority NORMAL:-KX-ALL:+SRP:+SRP-DSS:+SRP-RSA
$ gnutls-cli -p 4430 localhost --srpusername testuser --srppasswd 123456 --priority NORMAL:-KX-ALL:+SRP:+SRP-DSS:+SRP-RSA
$ curl --tlsuser testuser --tlspassword 123456 -k https://localhost:4430/

4.2 TLS + SCRAM

SCRAM 的支持程度比 SRP 好點,Dovecot SASL 和 Cyrus SASL 支持 SCRAM-SHA-1,GNU SASL 支持 SCRAM-SHA-1 和 SCRAM-SHA-1-PLUS。

跟 TLS-SRP 一樣,沒有 Web 瀏覽器直接支持 SCRAM-SHA-1-PLUS, 在 Web 瀏覽器上也需要 TLS x509 server cert 先建立 https 連接,然后用 HTML 表單以及 JavaScript 做 SCRAM-SHA-1 認證。同樣,也需要正規 CA 簽發的證書以避免 HTML 和 JS 被中間人替換掉。

非 Web 瀏覽器場合,可以不需要 X509 證書,在 TLS 上啟用 aNULL cipher() 加 SCRAM-SHA-1-PLUS(tls-unique channel binding)做認證。

4.3 TLS X509 server certificate + PLAIN

需要用正規 CA 簽名的 X509 證書,因為這個證書用來驗證服務端身份。

各種 SASL 實現都支持 PLAIN 機制,其實自己實現也非常簡單了,唯一要注意的是最好把認證服務跟業務邏輯所在服務分開,避免業務邏輯所在服務出簍子被人爬下整個密碼庫。

認證系統應該用 https 保護,並設置 HSTS 頭部:  。

4.4 OTP

OTP 的原理並不復雜,自己實現一個也不難,下面是許多現成的實現供參考。

 

 

  • OTPW: OPIE和S/KEY的替代品,但並不兼容。提供了 PAM module。其原理是生成幾百個隨機數,前頭拼一個 prefix password 然后計算RIPEMD-160 摘要值並按 BASE64 編碼顯示,用戶需要把這個密碼表打印出來。這個軟件設計思路以及給人的使用體驗都很古朴:-D
  • Google authenticator:  ,支持 TOTP 和 HOTP,提供了 PAM module,有 Android, iOS, Blackberry 版本的手機應用。Google 后來不提供它新版的源代碼了,所以有人做了兩個 fork:

 

沒有一個提供 backup code 特性,當然,這個不在 OTP 原理里頭,只是具體實現時的一個方便用戶的特性。實現時可以參考 Google authenticator 和 oath-toolkit。

使用 oath-toolkit 和 Google authenticator 可以驗證兩者是一致的,Google 返回的 seed 值是 16 個字符的 base32 編碼的字符串,實際上 Google authenticator 不要求必需是 16 個字符。

$ oathtool -b --totp 'bkuq 7tya sdbu jlda'  # 字符串的空格被忽略,大小寫無關
200157
$ oathtool -b --totp 'bkuq 7tya sdbu jlda' 200157   # 驗證
0

將那串 base32 編碼字符串輸入 Google authenticator 里,可以驗證它的結果跟 oathtool 生成的認證碼確實是一致的。Google authenticator 可以用於 Google 之外的服務。

5 總結

 

 

========== End

 


免責聲明!

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



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