加密技術的未來:從服務端密碼儲存到使用者資料加密方案

HD_Superman發表於2021-03-12

經作者授權轉載,原文連結,作者:Roronoa Zoro

本文主要講常見場景的資料加密方案,以及對未來加密技術的展望,先看幾條新聞:

Facebook 明文儲存使用者密碼:

Hundreds of millions of Facebook users had their account passwords stored in plain text and searchable by thousands of Facebook employees — in some cases going back to 2012, KrebsOnSecurity has learned. Facebook says an ongoing investigation has so far found no indication that employees have abused access to this data.

早在 2012 年,Facebook 明文儲存數億使用者的賬戶密碼,成千上萬的 Facebook 的員工可以隨意進行搜尋......

原文:Facebook Stored Hundreds of Millions of User Passwords in Plain Text for Years

CSDN 600萬使用者賬號密碼洩露:

北京時間12月21日晚間訊息,中國開發者技術線上社群CSDN今晚發表宣告,就“600萬使用者賬號密碼洩露”一事公開道歉,承認部分使用者賬號面臨風險,將臨時關閉使用者登入,並要求“2009年4月以前註冊的帳號,且2010年9月之後沒有修改過密碼”的使用者立即修改密碼。

原文:CSDN詳解600萬使用者密碼洩露始末:暫關閉登入

為什麼不能明文儲存密碼

很多新手程式設計師都是這樣儲存密碼的:

| username | phone | password | | -------- | ----------- | --------- | | 小明 | 18888888888 | asd123456 | | 大明 | 17777777777 | 123abc!@# |

為什麼這樣做是不安全的?

首先,如果遇到資料洩露事件,明文密碼直接將使用者隱私暴露在空中,任何人可以登陸暴露密碼的賬號,隨意更改。其次,即使不會洩露,內部員工也可以輕易訪問使用者的明文密碼,當公司上了規模,你無法保證公司內部沒有壞人,他們是否會搜尋某些使用者的密碼,侵犯使用者隱私。所以明文儲存密碼是絕對不安全的。

即使你用了這樣的密碼:ppnn13%dkstFeb.1st(娉娉嫋嫋十三餘,豆蔻梢頭二月初),明文儲存,安全性也是木有的。

再複雜的密碼, 也敵不過CSDN的明文

來自知乎使用者:Right Here

題外話:歷史上最有名的電腦密碼是什麼?

| 密碼 | 含義 | | -------------------------------------------- | --------------- | | FLZX3000cY4yhx9day | 飛流直下三千尺,疑似銀河下九天 | | hanshansi.location()!∈[gusucity] | 姑蘇城外寒山寺 | | hold?fish:palm | 魚和熊掌不可兼得 | | Tree_0f0=sprintf("2_Bird_ff0/a") | 兩個黃鸝鳴翠柳 | | csbt34.ydhl12s | 池上碧苔三四點,葉底黃鸝一兩聲 | | for_\$n(@ RenSheng)_\$n+="die" | 人生自古誰無死 | | while(1)Ape1Cry&&Ape2Cry | 兩岸猿聲啼不住 | | doWhile(1){LeavesFly();YangtzeRiverFlows()}; | 無邊落木蕭蕭下,不盡長江滾滾來 | | dig?F*ckDang5 | 鋤禾日當午 |

如何儲存 & 檢查密碼

既然密碼不能明文儲存,那怎麼儲存才是安全的?我如何檢查使用者輸入的密碼是正確的?

儲存相關資訊用於校驗是必須的,有沒有一種機制能夠只儲存密碼的部分資訊,也能用於密碼校驗?這樣即使資料庫洩露,攻擊者也無法通過這些資訊反推使用者的密碼,進而保護使用者賬號安全。

雜湊函式(hash function)可以解決這個問題。

雜湊函式是單向不可逆的,從上圖很好理解,經過 hash 函式都會被丟棄一部分資訊,就如同這個演算法:

演算法:儲存使用者名稱時丟棄使用者姓氏然後隨機打亂順序,輸入趙日天,輸出天日。

即使知道這個演算法和天日這個資料,也無法推斷出趙日天這個名字,因為部分資訊丟失了。

$$ h = hash(p) $$

h 為最終儲存到資料庫的值,p 為使用者原始密碼,在使用者登陸時,輸入密碼 p1,我們通過計算 $h_1 = hash(p_1)$,判斷 h1 是否與資料庫中的記錄 h 相同,確定使用者輸入的密碼是否正確。

所有雜湊函式都有一個性質:如果兩個 h 值是不一致的,那麼輸入 p 值也不一樣(單向雜湊函式),但另一方面輸入和輸出並非一一對應的關係,比如存在不同的 h 值,使得經過 hash 函式計算後的 p 值是一樣的。

雜湊函式就安全了嗎?

沒有,由於上述雜湊函式的性質,如果兩個使用者都用了123456abc,這樣的密碼,那麼資料庫儲存的 h 值都是一樣的,而且不同密碼可能計算出來的 h 值是一樣的(碰撞攻擊),那麼攻擊者就可以根據 hash 函式,暴力計算所有可能性,做成一張表格,這樣拿到 h 值的時候就可以推斷出密碼是 123456abc 了。這種做法叫彩虹表攻擊。

比如以前常用的雜湊函式 MD5,由於計算力逐步增強,現在已經是不安全的了:

MD5 1996年後被證實存在弱點,可以被加以破解,對於需要高度安全性的資料,專家一般建議改用其他演算法,如SHA-2。2004年,證實MD5演算法無法防止碰撞攻擊,因此不適用於安全性認證,如SSL公開金鑰認證或是數字簽名等用途

安全加固:加鹽

對付彩虹表可以用 Sated Hash 的方式,比如對於每個使用者密碼儲存時都記錄一個隨機的 salt 值(鹽值),用這個鹽值和密碼 p,計算出 h 值:

$$ h = hash(salt, p) $$

資料庫同樣儲存了 salt 值和 h 值,這樣攻擊者想獲取一個使用者的密碼,就得建立一個對應的彩虹表,增大攻擊者的成本。

但即使這樣,使用 SHA-2 再加鹽也不是安全的,因為計算力逐年提升,攻擊成本下降,有財力的攻擊集團還是能夠建立這些彩虹表,進而竊取使用者密碼。

安全加固:提高計算強度

加鹽的方式只是多了獨立的彩虹表,如果我們可以利用硬體控制每次 hash 計算的時間比如1秒,且無論用什麼機器,什麼高效能的CPU計算,每次都要1秒的時間,攻擊者要計算這個彩虹表,1000萬個組合就得115天(hash 空間遠遠超過 1000萬),那這種方式就很難破解了。

bcrypt 是一個由Niels Provos以及David Mazières根據Blowfish加密演算法所設計的密碼雜湊函式,於1999年在USENIX中展示[1]。實現中bcrypt會使用一個加鹽的流程以防禦彩虹表攻擊,同時bcrypt還是適應性函式,它可以藉由增加迭代之次數來抵禦日益增進的電腦運算能力透過暴力法破解。

除了對您的資料進行加密,預設情況下,bcrypt在刪除資料之前將使用隨機資料三次覆蓋原始輸入檔案,以阻撓可能會獲得您的計算機資料的人恢復資料的嘗試。如果您不想使用此功能,可設定禁用此功能。

除了 bcrypt 這種調整計算強度,抵禦日益增長的CPU 計算力帶來攻擊風險的演算法,scrypt 演算法還利用了記憶體空間,每次計算都要佔用一定內容,不過 bcrypt 演算法由於有成熟的實現,實際使用較多,spring boot security 的密碼加密就用的這個演算法。

比如某個密碼經過 bcrypt 加密後變成這樣:

\$2a\$07\$woshiyigesaltzhi\$\$\$\$\$.lrU488y7E1Xw.JA4uizIu.PBSSe7t4y

2a 表示 bcrypt 演算法的版本,07代表迭代次數,次數越高,每次計算所需的時間越長,後面的woshiyigesaltzhi$$$$$ 代表加密用的 salt 值,資料庫可以直接儲存這個欄位,比如:

| name | phone | pwd_hash | | ---- | ----- | -------------------------------------------------------------------- | | 小明 | 1234 | \$2a\$07\$woshiyigesaltzhi\$\$\$\$\$.lrU488y7E1Xw.JA4uizIu.PBSSe7t4y |

這種也是本文推薦的密碼儲存方式,hash + salt + 計算強度,可以較好保護使用者密碼安全,由於登陸不是頻繁的操作,每次登陸時使用者等待 1 秒也沒有太大關係。

使用者資料密碼加密方案:雙重 hash

密碼資訊可以 hash,達到不可逆的目的,但是一些使用者資料是可逆的,而且要求加密,怎麼辦?比如使用者線上文件,通過使用者自定義的密碼加解密。

很容易想到的就是 AES256 之類的對稱加密技術:

$$ e = AES256(salt, text) $$

通過拿使用者的密碼作為 salt 值加解密文件,服務端儲存使用者密碼。

還是前面的問題,明文儲存密碼是不安全的行為,這裡可以用雙重 hash 的方法保證一定的安全性(這裡的雙重 hash 不是連續計算兩次 hash 的意思):

實現方案

使用者密碼儲存仍然採用前面提到的方案,可以用 bcrypt 演算法,這裡記儲存的值為 h1,當使用者請求加密資料時,提供了加密密碼,我們通過 h1 檢驗使用者密碼的正確性,同時用使用者的密碼計算出另一個 hash 值,記為 h2,h2 的計算方式與 h1 不同,只是簡單的 hash,不能加鹽,這時我們使用 h2 加解密使用者文件:

$$ e = AES256(h2, text) $$

記住,h2 的值是不能儲存的(不能儲存於資料庫中),用於加解密資料。由於 h2 的值用完就丟掉。h2 的 hash 函式可以是私有的,進一步保障安全性。

為什麼需要 h2 這個引數?首先使用者密碼長度不一致,像 AES 之類的對稱加密演算法需要有一個固定長度的加密引數,其次經過 hash 之後能夠進一步保障資料安全性,如果資料庫洩露,攻擊者即使知道使用者密碼,不知道私有 hash 函式也無法進行解密。

其他使用者資料的加密

上述提到的加密方案是使用者密碼控制的,資料安全性非常高(只有使用者知道密碼,且密碼丟失後資料不可恢復),但是這種做法很多場景不適合,比如常規的使用者資料:手機號、社交賬號、地址、姓名,高頻訪問的資料不適合用密碼加密,效率太低,那如何保護使用者這類資料的安全性呢?

理解這個問題,需要知道使用者資料是怎樣傳輸的:

使用者在客戶端軟體上產生資料(比如瀏覽器上),經過 https 加密傳輸到後端伺服器,有服務端軟體處理(比如 java),之後經過呼叫資料庫介面通過資料庫軟體(比如 mysql)儲存到儲存裝置中(比如硬碟)。

有 4 個階段可以進行資料加密,加密的過程越靠近使用者越安全(客戶端加密後面說):

  • 服務端軟體加密:資料到達服務端後立馬加密儲存(記憶體中執行),比如 java 執行 AES256
  • 資料庫軟體加密:呼叫資料庫API實現資料庫加密,比如 mysql 的 AES 加密
  • 儲存端落盤加密:使用硬體加密技術進行加密儲存,比如雲服務商提供的雲盤加密功能

| 加密方式 | 防止內部洩露 | 防止資料庫洩露 | 防止物理機丟失洩露 | | ------- | -------- | ------- | --------- | | 服務端軟體加密 | √(大部分場景) | √ | √ | | 資料庫軟體加密 | | √ | √ | | 儲存端落盤加密 | | | √ |

前兩者加密方式都可以保證即使是資料庫管理員也無法檢視使用者資料,最後一種通常意義不大,但一些國家、地區的法律要求,或者使用者要求有硬碟加密措施,仍然需要使用。資料庫軟體加密仍然存在內部洩露的風險,比如 mysql 的 binlog,即使你使用了 AES256,資料同步時金鑰也會儲存在 binlog 中,存在洩露的途徑。

如果相關使用者資料沒有搜尋的需求(只有常規讀寫需求),可以使用服務端加密或者資料庫加密的手段保護使用者資料。但如果相關資料需要支援搜尋功能,這個問題就很棘手了。

可搜尋加密技術

這篇論文 Practical techniques for searches on encrypted data 發表於 2000 年,開創了一個新的研究方向 Searchable Encryption,提出了第一個實用的可搜尋加密方案 SWP,實現思路大概是:通過加密每個單詞,然後將一個 hash 值嵌入密文中,伺服器通過提取改 hash 值,檢查密文中是否有類似的特殊格式,確認是否匹配搜尋。

上文想法很理想,但是落地時會有很多困難,比如必須使用固定大小的單詞,但搜尋系統最重要的就是其分詞引擎,如何對多種語言進行良好的分詞直接決定了搜尋的效果,不少搜尋系統任然採用如下結構:

將 mysql 的資料單向同步到 elasticsearch 中,完成相關搜尋功能的支援。20 多年過去了,今天,可搜尋加密技術依然無法落地使用,甚至可以這麼說,如果軟體商提供了搜尋功能,該資料儲存的時候就是沒有加密的(硬碟加密對軟體層不可見),已加密的資料是無法支援搜尋功能的。

陰謀實現的前提

假如我是一個壞人,要實現某個陰謀,很重要的前提就是操作足夠簡單,知道的人足夠少,當複雜度太大或者需要的人很多的時候就不可能實現這個陰謀。

知道這一點我們可以很輕鬆地判斷:“美國登月是假的”,這個陰謀猜想是錯的。因為登月工程涉及的人很多、工程複雜,無法實現這個陰謀。

這個結論是下文內容的基礎。

一些軟體宣告的安全說的是什麼?

上述提到的可搜尋加密技術無法落地,一些軟體廠商(包括一些大廠)提供了搜尋功能,仍然聲稱他們對使用者資料進行了加密,很安全,他們到底在說什麼?

第一,他們可能說的是硬碟加密,而不是軟體層的加密(只能防禦硬碟盜竊風險,不能防禦資料洩露風險、內部風險);第二,他們可能說的是傳輸過程的加密,比如 https 或私有通訊加密協議;第三,他們可能有完善內部管理流程,控制內外部風險。

利用上面陰謀論的前提,只要增加一些流程,公開透明出來,提高使壞的成本,在技術不可實現的情況下,也能保障使用者資料的安全,比如:

  • 資料庫操作日誌審查:由其他人稽核 DBA 資料庫操作是否合規,是否偷偷檢視某使用者資料等
  • 多重密碼:由多人掌握密碼,只有統一輸入密碼才能解密資料,提高操作複雜度,如多人稽核
  • 公開透明的流程:涉密操作流程記錄,比如某某由於開發測試原因,需解密資料

擁有完善的流程也能讓使用者放心儲存隱私資料。

加密技術的未來:不用加密

前面沒有提到的客戶端加密放這裡說。加密點越靠近使用者,就越安全,如果使用者發出來的資料就是經過加密的(非 https 類傳輸加密),並且自行控制密碼,這樣服務提供商也無需進一步做加密處理了,標題提到的不用加密指的就是這個,服務商不用再花費大量精力、成本在資料安全上面,將資料所有權交給使用者。

上面客戶端加密所需的一個技術就是:全同態加密,這是密碼學領域的一個重要課題,在 2009 年 9 月,IBM 的博士克雷格·金特里發表了一篇論文:Fully Homomorphic Encryption Using Ideal Lattices,提出了一個可行方法,自此解決了密碼學的一大難題。全同態加密可以簡單理解如下:

$$ f(data) = DE(\ f(\ E(data)\ )) $$

其中 f 是任意操作函式,E 是加密函式,DE 是解密函式,也就是說對密文的任意計算操作等同於對明文做同樣的操作。

這就很牛逼了!!!!!!!!!!!比如我手上有一些財務資料,需要第三方機構進行統計分析,但是我又不想直接給他們資料,他們告訴我可以提供全同態加密服務,那麼我可以在給他們資料之前將資料加密一遍,他們給我統計計算後的結果,我解密相關結果即可,真實的資料只有我看得到。

全同態加密的這些特效能夠很好解決資料安全,信任的問題,克雷格·金特里給出了一個實現,之後很多密碼學者也給出其他實現方法,但目前的角度來說該技術還不成熟,比如一個金鑰就得 100 MB 大小,對於當下網路環境來說無法實際使用。

結語

沒有絕對的安全,只有相對的安全。期待全同態加密技術在商用領域取得突破性進展。

相關文章