從客戶端-伺服器看安全#
用客戶端與伺服器的角度來思考,讓分散式系統變安全基本上可以歸結為兩大議題:
- 通訊安全:如何讓客戶端與伺服器之間的通訊安全。這需要對通訊雙方做身分驗證,多數情況下還需要確保訊息完整性,可能也要求機密性;此外也要保護一組伺服器之間的群組通訊。
- 授權:伺服器接受了客戶端的請求後,如何確認該客戶端有權要求執行這個請求。授權與資源存取控制相關,留到下一節;本節專注於保護通訊。
保護通訊雙方的問題,可以用建立**安全通道(secure channel)**來思考(Voydock and Kent, 1983):
安全通道保護傳送者與接收者免受訊息的攔截、竄改與偽造——但不必然防護中斷。防攔截靠機密性(訊息無法被竊聽);防竄改與偽造靠相互驗證與訊息完整性的協定。
身分驗證#
首先要注意:身分驗證與訊息完整性缺一不可。
- 只有驗證、沒有完整性:Bob 確知訊息 m 是 Alice 送的,但若無法保證 m 傳輸途中沒被改過,知道發送者是誰又有何用?
- 只有完整性、沒有驗證:Bob 收到「你中了 100 萬美元」的訊息,若無法確認訊息真的來自樂透主辦單位,他能高興得起來嗎?
典型的組合方式是:Alice 先送訊息給 Bob(或給協助建立通道的可信第三方)以建立通道;通道建立、雙方都確認對方身分後,再交換資料訊息。驗證完成後的資料完整性(可能加上機密性),通常改用秘密金鑰密碼學、透過**會談金鑰(session key)**來確保——這是一把只在通道存續期間使用的共享秘密金鑰,通道關閉後即安全銷毀。
以共享秘密金鑰驗證#
假設 Alice(A)與 Bob(B)已共享秘密金鑰 K_A,B(如何安全取得共享金鑰,本章稍後討論)。協定採取**挑戰-回應(challenge-response)**的常見做法:一方向另一方拋出挑戰,只有知道共享秘密金鑰的人才答得出來。五訊息版協定如下:
- Alice 送出自己的身分 A,表示想建立通道。
- Bob 回一個挑戰
R_B(例如一個亂數)。 - Alice 用
K_A,B加密挑戰,回傳K_A,B(R_B)。Bob 解密後若看到R_B,就確認對面是 Alice——除了 Alice 還有誰能用K_A,B加密R_B? - 但 Alice 還沒驗證對面是 Bob,於是她送出挑戰
R_A。 - Bob 回傳
K_A,B(R_A),Alice 解密看到R_A,確認對面是 Bob。

圖 9-12:以共享秘密金鑰為基礎的身分驗證。
反射攻擊:協定「優化」的陷阱#
安全領域較難的課題之一,是設計出真正管用的協定。考慮一個把訊息數從五減為三的「優化」:Alice 建立通道時就順便帶上自己的挑戰(送出 A, R_A),Bob 則在一則訊息裡同時回覆回應與自己的挑戰。

圖 9-13:同樣以共享秘密金鑰為基礎的身分驗證,但只用三則訊息而非五則。
這個三訊息版協定會被**反射攻擊(reflection attack)**擊破。入侵者 Chuck 不知道
K_A,B,卻能騙 Bob 替他加密:
- Chuck 冒名送出
A, R_C給 Bob。- Bob 回覆挑戰
R_B與回應K_A,B(R_C)。- Chuck 此刻答不出
K_A,B(R_B),於是再開第二條通道,送出A, R_B——把 Bob 自己的挑戰丟回給他。- Bob 沒認出
R_B是自己剛用過的挑戰,照樣回覆K_A,B(R_B)與新挑戰R_B2。- Chuck 拿著 Bob 親手加密的
K_A,B(R_B)回到第一條通道完成建立。Bob 從此以為自己在跟 Alice 說話。

圖 9-14:反射攻擊。
從這次失敗可以整理出幾條協定設計原則(Kaufman et al., 2003):
- 發起方與回應方應使用不同的挑戰。例如 Alice 只用奇數、Bob 只用偶數,Bob 就會發現訊息 3 裡的
R_B有鬼。(可惜這個修法仍擋不住其他攻擊,特別是「中間人攻擊(man-in-the-middle attack)」,見 Ferguson and Schneier, 2003。)一般而言,讓建立安全通道的兩方做一模一樣的事並不是好主意。 - 在確認對方身分之前,不要交出有價值的資訊。改編版協定中,Bob 在不知道對方是誰的情況下就交出了
K_A,B(R_C);原始五訊息版沒有這個問題——Alice 得先證明身分,Bob 才願意回傳加密資訊。
設計出真正符合預期的安全協定,往往比表面看起來困難得多;為了效能微調既有協定,很容易破壞其正確性。更多協定設計原則見 Abadi and Needham (1996)。
使用金鑰發配中心(KDC)驗證#
共享秘密金鑰驗證有擴充性問題:N 台主機兩兩共享金鑰,全系統要管理 N(N-1)/2 把金鑰,每台主機要管 N-1 把。集中式的替代方案是金鑰發配中心(Key Distribution Center, KDC):KDC 與每台主機各共享一把秘密金鑰,主機彼此之間不需共享——全系統只需管理 N 把金鑰。
基本流程:Alice 告訴 KDC 想跟 Bob 通話,KDC 產生共享金鑰 K_A,B,用 K_A,KDC 加密後交給 Alice,同時用 K_B,KDC 加密後交給 Bob。

圖 9-15:使用 KDC 的原理。
- 缺點:Bob 可能還沒收到金鑰,Alice 就開始建立通道了;而且 KDC 得主動把金鑰塞給 Bob。
- 改良:KDC 把
K_B,KDC(K_A,B)也交給 Alice,由 Alice 自己轉交 Bob。這個訊息稱為票證(ticket)——只有 Bob(與 KDC)能解開它,所以由 Alice 經手也無妨。

圖 9-16:使用票證,並讓 Alice 自己去與 Bob 建立連線。
這個改良版其實是知名的 Needham-Schroeder 驗證協定(Needham and Schroeder, 1978)的變形;Kerberos 系統採用的是它的另一種變形。Needham-Schroeder 是多路挑戰-回應協定:
- Alice 送給 KDC:挑戰
R_A1、自己的身分 A 與 Bob 的身分 B。 - KDC 回覆(以
K_A,KDC加密):R_A1、B、金鑰K_A,B,以及票證K_B,KDC(A, K_A,B)。 - Alice 把票證連同以
K_A,B加密的挑戰R_A2送給 Bob。 - Bob 解開票證取得
K_A,B,回覆R_A2 - 1與自己的挑戰R_B(以K_A,B加密)。 - Alice 回覆
K_A,B(R_B - 1)。

圖 9-17:Needham-Schroeder 驗證協定。
協定中每個欄位都有存在的理由:
R_A1是臨時亂數(nonce)——只用一次的隨機數,通常取自極大的集合,用途是把訊息 1 與訊息 2 唯一地關聯起來。若沒有 nonce:假設 Chuck 偷到 Bob 的一把舊金鑰,又攔截過 KDC 先前回給 Alice 的舊回覆;即使 Bob 後來已與 KDC 換了新金鑰,Chuck 只要重放舊回覆,就能騙 Alice 相信自己在跟 Bob 說話(他能解開舊票證、證明知道舊的K_A,B)。有了 nonce,重放的舊訊息裡的 nonce 對不上新請求,立刻穿幫。- 訊息 2 帶上 B 的身分,是為了防止另一種攻擊:若少了 B,Chuck 可以把訊息 1 裡的 Bob 換成自己(C),KDC 便會替「Alice 與 Chuck」發金鑰;等 Alice 去找 Bob 時 Chuck 從中攔截,Alice 就被騙了。KDC 把對方身分抄回訊息 2,Alice 便能立刻發現請求被改過。
- 訊息 4 回
R_A2 - 1而非R_A2:Bob 不只證明自己知道共享金鑰,還證明他確實解開了挑戰,把訊息 4 綁到訊息 3。(其實此處回K_A,B(R_A2, R_B)就夠了,因為這則訊息先前沒在協定中出現過;回R_A2 - 1是歷史因素。)
延伸:修補 Needham-Schroeder 的最後弱點
上述協定仍有一個弱點:若 Chuck 拿到一把舊的 K_A,B,他可以重放訊息 3,讓 Bob 建立通道並誤以為對面是 Alice。問題出在訊息 3 沒有綁到訊息 1——金鑰與 Alice 的原始請求無關。
解法是在 Alice 給 KDC 的請求中放進一個來自 Bob 的 nonce:Alice 先請 Bob 給她一個以 K_B,KDC 加密的 nonce R_B1;Alice 把它一併送給 KDC,KDC 解密後把結果放進產生的票證裡。如此 Bob 就能確定:這把會談金鑰確實綁定於 Alice 要跟他通話的那次原始請求,重放舊票證再也騙不了他。

圖 9-18:防止先前產生的會談金鑰被惡意重複使用。
使用公鑰密碼學驗證#
不需要 KDC 的做法:假設 Alice 與 Bob 都持有對方的公鑰。
- Alice 用 Bob 的公鑰
K+_B加密挑戰R_A送出。只有 Bob 能用對應私鑰解開,所以能答對就是 Bob。 - Bob 回覆解開的
R_A、自己的挑戰R_B,以及他產生的會談金鑰K_A,B,全部用 Alice 的公鑰K+_A加密。只有 Alice 解得開這則訊息。 - Alice 用會談金鑰
K_A,B回覆對R_B的回應,證明她解得開訊息 2,確實是 Alice 本人。

圖 9-19:公鑰密碼系統中的相互驗證。
這個協定成立的前提是 Alice 確定拿到的是 Bob 的公鑰,而不是某個冒充者的公鑰。如何提供這種保證(憑證),本章稍後討論。
訊息完整性與機密性#
安全通道除了驗證,還要保證訊息完整性(防止暗中竄改)與機密性(防止被竊聽讀取)。機密性容易:送出前把訊息加密即可,用與接收者共享的秘密金鑰或接收者的公鑰皆可。完整性則複雜一些。
數位簽章#
訊息完整性的需求常超出傳輸當下。假設 Bob 透過電子郵件以 500 美元把一張唱片賣給 Alice,Alice 寄出確認訊息。除了驗證之外還有兩個完整性問題:
- Alice 要確保 Bob 不能把訊息裡的 500 美元惡意改高,再宣稱她答應了更高的價錢。
- Bob 要確保 Alice 事後不能否認寄過這則訊息(例如她反悔了)。
兩者都能用**數位簽章(digital signature)**解決:讓 Alice 對訊息簽章,且簽章與訊息內容唯一綁定——訊息被改就會被發現;簽章驗證為真,Alice 就無法否認(non-repudiation)簽過。
常見做法是用 RSA 這類公鑰系統:
- Alice 把訊息 m 用自己的私鑰
K-_A加密(即簽章)後送給 Bob;若同時要保密,可再用 Bob 的公鑰包一層,送出K+_B(m, K-_A(m))。 - Bob 用 Alice 的公鑰解開簽章版本,與明文 m 比對成功,即可確認訊息來自 Alice。Bob 若想竄改 m,就得證明改過的版本也有 Alice 的簽章,辦不到;Bob 也應保留簽章版本,防 Alice 否認。

圖 9-20:以公鑰密碼系統為訊息簽章。
協定本身正確,但這個方案有幾個實務問題:
- 簽章的效力取決於 Alice 的私鑰保持秘密。Alice 想毀約時,可以宣稱私鑰在訊息送出前就被偷了。
- Alice 更換私鑰(定期換鑰本是防入侵的好習慣)後,先前的簽章聲明就失效了。這可能需要一個中央權威記錄換鑰時間,並在簽章時加上時間戳。
另一個問題是成本:用私鑰加密整則訊息運算昂貴(若訊息當作二進位數超過上限,甚至在數學上不可行),而且沒有必要——我們只需要把簽章與訊息唯一關聯。更便宜也更優雅的做法是簽訊息摘要(message digest):
- Alice 先用密碼雜湊函數 H 算出固定長度的摘要
h = H(m),再用私鑰加密摘要,連同明文訊息一起送出(人人可讀;若需機密性,再用 Bob 的公鑰加密訊息)。 - Bob 用 Alice 的公鑰解開摘要,另外自己對收到的訊息算一次摘要;兩者相符,即知訊息由 Alice 簽署。若 m 被改成 m’,
H(m')就與 h 不符,竄改立刻被偵測。

圖 9-21:使用訊息摘要對訊息進行數位簽章。
會談金鑰#
安全通道建立、驗證階段完成後,通訊雙方一般改用唯一的共享會談金鑰來確保機密性,通道不再使用時即安全丟棄。為什麼不直接沿用建立通道時的金鑰?理由如下(Kaufman et al., 2003):
- 金鑰會「磨損」:金鑰用得越多越容易洩底。入侵者攔截到大量以同一把金鑰加密的資料後,就可能發動攻擊找出金鑰特徵,甚至還原明文或金鑰本身。驗證金鑰應盡量少用——何況這類金鑰常靠郵寄、電話等昂貴的頻外(out-of-band)方式交換,更該省著用。
- 防重放:每次建立通道都用一把全新的會談金鑰,至少能防止整段會談被重放。(要防個別訊息被重放,還需要時間戳或序號等額外措施。)
- 損害控制:若完整性與機密性都用建立通道的那把金鑰,金鑰一旦洩漏,入侵者連舊會談的訊息都能解密。改用每會談一把金鑰,最壞情況只賠上一個會談,其他會談的訊息仍保持機密。
- 信任分級:Alice 可能想跟 Bob 交換一些機密資料,但又沒信任到願意用長期金鑰加密資料給他——長期金鑰她想留給真正信任的對象。用一把相對廉價的會談金鑰跟 Bob 說話就夠了。
驗證金鑰的更換通常代價高昂,因此「長期金鑰+廉價短期會談金鑰」的組合,往往是實作資料交換安全通道的好選擇。
安全群組通訊#
分散式系統中常需要兩方以上的安全通訊,典型如複寫伺服器:所有副本之間的通訊都要防竄改、防偽造、防攔截。
機密的群組通訊#
保護 N 個使用者的群組通訊不被竊聽,有三種方案:
- 全組共用一把秘密金鑰:所有訊息用它加解密。前提是每個成員都被信任會保密——光這個前提就讓它比兩方通道更脆弱。
- 每對成員一把金鑰:某成員洩密時,其他人只要停止傳訊給他即可,其餘金鑰照用。代價是要維護
N(N-1)/2把金鑰,本身就是難題。 - 公鑰系統:每個成員一對公私鑰,所有人都可用某成員的公鑰傳機密訊息給他,共 N 對金鑰。成員不再可信時直接踢出群組,不會殃及其他金鑰。
安全的複寫伺服器#
換一個問題:客戶端對一組複寫伺服器發出請求,即使其中若干伺服器已被入侵者收買,客戶端仍期望回應可信。直觀解法是收集所有伺服器的回應並一一驗證,取未被收買(已驗證)伺服器的多數——但這暴露了複寫的存在,違反複寫透明性(replication transparency)。
Reiter et al. (1994) 提出保有複寫透明性的方案,核心是秘密分享(secret sharing):多方共享一個秘密時,沒有任何一方知道完整的秘密,只有湊在一起才能揭露。(經典例子:發射核彈通常需要至少兩人授權,各持一把私鑰,單獨一把無效。)
目標:最多 k 台伺服器可能給出錯誤答案,其中最多 c <= k 台真的被入侵者收買——這使服務本身是 k 容錯的,差別在於我們把被惡意收買的伺服器也歸類為故障。做法(以主動複寫為例):
- 每台伺服器
S_i處理請求後,把回應r_i連同簽章sig(S_i, r_i) = K-_i(md(r_i))一起送出(md(r_i)是回應的訊息摘要,以伺服器私鑰簽署)。 - 若要容忍最多 c 台被收買的伺服器,就設計成至少 c + 1 個簽章才能組出有效簽章:c 台被收買的伺服器聯手也湊不出來。
- 以 5 台伺服器容忍 2 台被收買為例:客戶端收到 5 個三元組
(r_i, md(r_i), sig(S_i, r_i))。因為沒有任何單一伺服器可信,客戶端改用一個公開的特殊解密函數 D,它以三個簽章的集合 V 為輸入、輸出單一摘要。5 取 3 共有 10 種組合,若某個組合算出的摘要與某個回應r_i的摘要相符,客戶端即可信任r_i——它必然出自至少三台誠實的伺服器。

圖 9-22:在一組複寫伺服器中分享一個秘密簽章。
為了進一步提升複寫透明性,Reiter 與 Birman 讓每台伺服器把 (r_i, sig(S_i, r_i)) 廣播給其他伺服器;收齊至少 c + 1 則訊息(含自己的)後,伺服器嘗試為某個回應組出有效簽章,成功則把該回應 r 與簽章集合 V 打包成單一訊息送給客戶端,客戶端只需驗證 md(r) = D(V)。
這就是所謂 (m,n) 門檻方案((m,n)-threshold scheme)(此例中
m = c + 1、n = N):訊息被分成 n 份,稱為 shadow,任何 m 份即可重建原始訊息,m − 1 份以下則不行。建構方式有多種,詳見 Schneier (1996)。
範例:Kerberos#
把安全整合進分散式系統並不容易——整個系統都得安全,任何一處不安全,全系統就可能淪陷。為此,人們開發了一些可作為基礎的支援系統,其中被廣泛使用的重要系統是 Kerberos(Steiner et al., 1988; Kohl and Neuman, 1994)。
Kerberos 由 M.I.T. 開發,以前述 Needham-Schroeder 驗證協定為基礎。目前有 V4 與 V5 兩個版本,概念相似,V5 更有彈性、更可擴充(詳見 Neuman et al., 2005;實務操作見 Garman, 2003)。Kerberos 協助客戶端與系統中的任何伺服器建立安全通道,安全性建立在共享秘密金鑰之上,包含兩個元件:
- 驗證伺服器(Authentication Server, AS):處理使用者登入請求,驗證使用者並提供用來建立安全通道的金鑰。
- 票證核發服務(Ticket Granting Service, TGS):核發票證(ticket)——用來向伺服器證明客戶端確為其所宣稱身分的特殊訊息。
登入流程#
Alice 在任一工作站登入:
- Alice 在工作站輸入登入名稱。
- 工作站把名稱以明文送給 AS。
- AS 回覆會談金鑰
K_A,TGS與票證K_AS,TGS(A, K_A,TGS);整則訊息以 Alice 與 AS 共享的K_A,AS加密。票證之後要由 Alice 轉交 TGS,但只有 TGS 解得開(它以 AS 與 TGS 共享的K_AS,TGS加密)。 - 工作站此時才提示 Alice 輸入密碼,用密碼產生共享金鑰
K_A,AS(例如對密碼字串做密碼雜湊,取前 56 位元當金鑰),解開訊息 3。
這個設計有兩個優點:Alice 的密碼從未以明文上網,且工作站連暫存都不需要——一旦算出
K_A,AS、取得K_A,TGS,密碼即可拋棄。
登入後,AS 發的票證會暫存(一般 8–24 小時)供存取遠端服務使用。當 Alice 想跟伺服器 Bob 通話:
- 她向 TGS 請求對 Bob 的會談金鑰(訊息 6),附上票證
K_AS,TGS(A, K_A,TGS)(持有此票證就證明她是 Alice),以及用K_A,TGS加密的時間戳 t。 - TGS 驗證時間戳:與當前時間差超過幾分鐘就拒發票證——這防止 Chuck 惡意重放訊息 6 去跟 Bob 建通道。
- TGS 回覆會談金鑰
K_A,B,同樣封裝在一張 Alice 之後要轉交 Bob 的票證裡。

圖 9-23:Kerberos 中的身分驗證。
這個架構實現了單一登入(single sign-on):只要 Alice 不換工作站,就不需要再向系統中任何其他伺服器驗證身分。伺服器等於把客戶端驗證委託給 AS 與 TGS,願意接受任何持有效票證的客戶端請求。(遠端登入之類的服務當然仍要求使用者有帳號,但那與 Kerberos 驗證無關。)注意:Alice 離開工作站時應銷毀所有快取的票證。
與 Bob 建立安全通道#
- Alice 送給 Bob:TGS 發的票證
K_B,TGS(A, K_A,B)與以K_A,B加密的時間戳。Bob 解開票證後知道對面是 Alice——因為只有 TGS 能造出這張票證;他同時取得K_A,B,可驗證時間戳,確定這不是重放。 - Bob 回覆
K_A,B(t + 1),向 Alice 證明自己確實是 Bob。
