安全與密碼學
去年的安全和隱私講座重點討論了如何以電腦 使用者 的身份提高安全性。 今年,我們將重點放在和本課程以前介紹的工具有關的,在安全性和密碼學概念方面的知識。 例如,在 Git 中使用 hash 函式或在 SSH 中使用密鑰派生函式和對稱/非對稱密碼系統。
此課程不可作為計算機系統安全(6.858) 或密碼學 (6.857 與 6.875)的替代。 不要在沒有接受良好教學的情況下從事安全相關工作。 除非你是專家,不要自創加密方法, 這在系統安全方面是同樣的。
這節課對基本密碼學概念有著非常簡略(但我們認為很有用)的介紹。 若你想 設計 安全系統或加密協議,僅僅學習本課是不夠的。 但我們希望此課可以讓你大致瞭解已經廣泛使用的程式與協議。
熵(Entropy)
熵是對隨機性的一種度量。 在測試密碼強度等環境下,熵有著重要作用。
如同上面 XKCD 漫畫 描繪的,”correcthorsebatterystaple” 比 “Tr0ub4dor&3” 更為安全,我們該如何判斷安全性?
熵以 位元 度量,對於一個均勻分佈的隨機變數,熵等於 log_2(可能性總數)
。
擲一次硬幣的熵爲 1 位元,擲一次(六面)骰子的熵約為 2.58 位元。
我們通常認為攻擊者瞭解密碼的 模型, 但是不知道密碼是如何隨機選出的(例如 擲骰子)。
多少位元的熵是足夠安全的?這通常取決於威脅模型。上面的 XKCD 漫畫指出,對於線上密碼攻擊,大約 40 位元的熵即可。對於線下攻擊,需要 80 位元或更強的密碼。
密碼雜湊函式
密碼雜湊函式 會將任意大小的資料對映至指定大小。一個粗略的雜湊函式規範大概是這樣的:
hash(value: array<byte>) -> vector<byte, N> (N 是指定數目)
Git 使用的 SHA1
即是雜湊函式。它會將任意大小的輸入對映至 160 位元(即 40
個十六進位字元)中。我們可以使用 sha1sum
指令執行它:
$ printf 'hello' | sha1sum
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
$ printf 'hello' | sha1sum
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
$ printf 'Hello' | sha1sum
f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0
更通用的解釋是,雜湊函式可以被認為是不可逆且隨機(但是具有確定性)的函式(這是雜湊函式的理想模型)。 一個雜湊函式將會有如下性質:
- 確定性: 對於相同輸入,輸出也是相同的
- 不可逆: 對於函式
hash(m) = h
,給出h
幾乎不可能求得對應的m
。 - 弱抵抗/低碰撞抗性(Target collision resistant): 給予輸入
m_1
, 很難找到m_2
使得hash(m_1) = hash(m_2)
。 - 強抵抗/高碰撞抗性(Collision resistant): 很難找到兩個輸入
m_1
和m_2
使得hash(m_1) = hash(m_2)
。注意這比弱抵抗更為嚴格。
注意:雖然對於一般用途還有效,SHA-1 已經不再被認為是強密碼雜湊函式了,你也許會對密碼雜湊函式的生命週期感興趣。對於特定需求推薦指定雜湊函式不在此課程的涵蓋範圍內,如果你有這些需求,需要學習安全學或密碼學。
運用
- Git,用於內容定址儲存。雜湊函式是一個寬泛的概念(存在與密碼學無關的雜湊函式)。為什麼 Git 要使用它?
- 檔案資料摘要。例如 Linux ISO 這種軟體經常需要從(潛在不可信的)非官方映象下載,此時不信任它們比較好。官方站點通常會在映象站點旁邊列出 hash,這樣你可以在下載後檢驗得到的檔案。
- 承諾方案(Commitment scheme)。假定你需要承諾一個值,但要之後揭示它。例如,我要“在想象中”擲一次硬幣,但是這個硬幣對你不可見也不受信任。我可以選擇一個值
r = random()
,然後告訴你h = sha256(r)
。我們同意r
為偶數時代表正面,奇數時代表反面。之後,你可以選擇正面還是反面。判斷勝負後,我可以告訴你我的值r
,並且可以通過檢驗sha256(r)
來確認我沒有作弊。
密鑰衍生函式
密鑰衍生函式 (KDFs) 作為密碼雜湊函式的相關概念,被運用於多個方面,包括生成可以在其他密碼演算法中使用的固定長度密鑰等。 為了對抗線下的窮舉攻擊,KDFs 通常故意被設計成緩慢執行。
運用
- 生成可以在其他加密演算法中使用的密鑰。(例如對稱加密,下面會講解)。
- 儲存登入憑據時,儲存明文密碼是不適宜的。正確的方法是對每個使用者生成並儲存鹽
salt = random()
,以及KDF(password + salt)
,並在每次登入後檢驗它。
對稱加密
當你需要加密時,最先想到的應該是隱藏明文資訊,對稱加密使用以下幾種方法來實現此功能:
keygen() -> key (這是一個隨機方法)
encrypt(plaintext: array<byte>, key) -> array<byte> (獲得密文)
decrypt(ciphertext: array<byte>, key) -> array<byte> (獲得明文)
透過加密方法獲得的輸出(密文)很難在不知道 key 的情況下解得其輸入(明文)。
解密方法具有明顯的正確性,即 decrypt(encrypt(m, k), k) = m
一定成立。
一個現在被廣泛使用的對稱加密例子是 AES。
運用
- 在不信任的雲服務上加密檔案。可以與密碼雜湊函式共同使用。透過
key = KDF(passphrase)
生成一個密鑰,然後在雲上儲存encrypt(file, key)
。
非對稱加密
“非對稱”是指存在兩個持有不同職責的密鑰。 顧名思義,私鑰旨在保持私密,而公鑰可以公開共享,且不會影響安全性(與在對稱加密系統中共享密鑰不同)。 非對稱密碼系統提供以下功能,以進行加密/解密和簽章/檢驗:
keygen() -> (public key, private key) (這是一個隨機方法)
encrypt(plaintext: array<byte>, public key) -> array<byte> (獲得密文)
decrypt(ciphertext: array<byte>, private key) -> array<byte> (獲得明文)
sign(message: array<byte>, private key) -> array<byte> (獲得簽章)
verify(message: array<byte>, signature: array<byte>, public key) -> bool (檢驗此簽章為有效的)
加密/解密功能類似於對稱加密系統的性質。
可以使用 公共 密鑰對訊息進行加密。
給定輸出(密文),沒有 私有 密鑰就很難確定輸入(明文)。
解密函式具有明顯的正確性,即 decrypt(encrypt(m,public key),private key)= m
一定成立。
對稱和非對稱加密與生活中的鎖類似。 對稱加密系統就像門鎖:任何擁有鑰匙的人都可以鎖定和解鎖。 非對稱加密就像帶鑰匙的掛鎖。 您可以將鎖本身(公鑰)提供給某人,他們可以在盒子內放入訊息,然後鎖上它。 只有你可以開啟該鎖,因為你持有鎖的鑰匙(私鑰)。
簽章/檢驗功能與書面簽名類似,即難以偽造。
在不知道 私鑰 的情況下,很難得出一個可以通過 verify(message, signature, public key)
檢驗的簽章。
對於使用私鑰簽章的資料,verify(message, sign(message, private key), public key) = true
這種檢驗方式在提供正確資料時一定成立。
運用
- PGP 電郵加密。使用者可以在網路上釋出公鑰 (例如一個 PGP 密鑰伺服器,或者 Keybase)。任何人都可以向他們傳送加密電郵。
- 私密通訊。 類似 Signal 與Keybase 這樣的程式使用非對稱加密系統來建立私密通訊。
- 程式簽章。Git 支援帶有 GPG 簽章的提交。透過開發人員釋出的公鑰,任何人都可以驗證下載到的軟體。
密鑰分配
非對稱加密很棒,但是在分配公鑰/將公鑰對應至現實中的個體時面臨著很大的挑戰。有許多解決此問題的方法。 Signal 提供了簡單地解決方案:使用者第一次使用時信任其身份,並支援線下的公鑰交換(你需要親自驗證朋友是否安全)。 PGP有另一種解決方案,即信任網。 Keybase 主要使用社交證明,與一些其他精妙方法。每一種模型都有其優勢,我們(講師)喜愛 Keybase 的模型。
案例
密碼管理器
任何人都應該試一試密碼管理器(例如 KeePassXC)。 密碼管理器會幫助你對每個網站生成隨機且高熵的密碼。它會將你的所有密碼儲存至一處,且透過 KDF 利用你的主密碼生成的密鑰進行對稱加密。
密碼管理器會讓你避免復用密碼(所以當網站資料洩漏時你受到影響較少),且使用高熵密碼(使你的密碼更難被竊取)。 密碼管理器只需要你記住一個高熵的主密碼。
雙重驗證
雙重驗證 (2FA) 要求你同時使用密碼(“你知道的資料”)和一個 2FA 驗證器(比如YubiKey,“你擁有的東西”)來對抗密碼洩漏與釣魚攻擊。
全磁碟加密
將筆電全磁碟加密是在其被盜時保護資料的簡單辦法。 Linux 系統下使用 cryptsetup + LUKS, Windows 下使用 BitLocker, macOS 下使用 FileVault。 它們會通過對稱加密來保護整個硬碟。
私密通訊
使用 Signal 或 Keybase。 非對稱密鑰加密會提升端到端安全性。獲取聯絡人的公鑰是此處的關鍵步驟。 如果想要良好的安全性,需要線下(通過 Signal 或 Keybase )對公鑰進行身份驗證,或者信任社交證明(透過 Keybase)。
SSH
之前的講座涉及了 SSH 與 SSH 密鑰的使用,讓我們從密碼學方面看看他們。
當你執行 ssh-keygen
時候,它會給出一組非對稱密鑰,即public_key, private_key
。
這使用作業系統(從硬體事件擷取的)熵隨機生成。公鑰會被明文儲存(它是公開的,所以保密性不重要),不過私鑰必須加密。
ssh-keygen
程式會要求使用者輸入一個密碼,並透過密鑰生成函式來使用該密碼生成一個密鑰,ssh-keygen
會來利用此密鑰透過對稱加密方式加密私鑰。
在現實場景中,當伺服器已知使用者的公鑰(儲存在 .ssh/authorized_keys
),
與其連結的客戶端可以透過非對稱簽章證明其身份。這個過程由質詢-應答機制完成。
通常來說,伺服器選擇一個隨機數字,並傳送至客戶端。客戶端對此數字簽章後將其返回伺服器。伺服器隨後使用公鑰驗證此返回資訊是否由對應的私鑰簽章。這個過程可以證明伺服器中
.ssh/authorized_keys
裏儲存的公鑰與該客戶端的私鑰是對應的,伺服器便可放行客戶端登入。
資源
- 去年的講座: 更注重於使用者如何增強隱私保護並保證安全
- Cryptographic Right Answers: 解答了許多常見場景下的 “對於此場景,我該如何選擇加密方式” 的問題
課後練習
- 熵
- 假定一個密碼是由 5 個小寫的常規詞拼接組成,每個詞都是從含有 100,000 詞的字典內隨機選取,且每個詞選中概率相同,
例如
correcthorsebatterystaple
。這個密碼的熵是多少位元? - 假定一個密碼由 8 個隨機字母數字組成(包括大小寫),例如
rg8Ql34g
,它的熵是多少位元? - 以上兩個密碼誰更強?
- 假定一個入侵者可以每秒嘗試 10,000 個密碼,平均來說,他需要多久來攻破上面兩個密碼?
- 假定一個密碼是由 5 個小寫的常規詞拼接組成,每個詞都是從含有 100,000 詞的字典內隨機選取,且每個詞選中概率相同,
例如
- 密碼雜湊函式 從
Debian 映象站下載一個 Debian 映象
(比如這個阿根廷映象站)。
檢查 hash 值(例如使用
sha256sum
指令),與域名為debian.org
的 Debian 官方站點提供的值(例如這個檔案)進行對比。 - 對稱加密 透過 OpenSSL 使用 AES 加密一個檔案:
openssl aes-256-cbc -salt -in {input filename} -out {output filename}
。 使用cat
或hexdump
檢視其內容。再使用openssl aes-256-cbc -d -in {input filename} -out {output filename}
來解密,並且使用cmp
驗證解密內容與元檔案相同。 - 非對稱加密
- 在你擁有的電腦上(不要使用 Athena,因為 Kerberos 與 SSH 密鑰的互動十分詭異)設定一個 SSH 密鑰。使用更安全的 ED25519 密鑰來替代 RSA 密鑰會更好。確保你使用了密碼來加密私鑰。
- 設定 GPG。
- 給 Anish 傳送一封加密電郵 ( Anish 的公鑰)。
- 使用
git commit -S
簽署一個提交,或使用git tag -s
建立一個帶有簽章的 Git tag。 使用git show --show-signature
來檢查提交的簽章,或使用git tag -v
檢查 tag。
Licensed under CC BY-NC-SA.