前面章節討論過的許多安全原則,都直接應用於分散式檔案系統。主從式架構下的做法是由伺服器負責認證(authentication)與存取控制(access control),這是最直接的處理方式,NFS 等系統即採此路線。常見配置是用獨立的認證服務(如 Kerberos),檔案伺服器只處理授權。

這種方案的主要缺點是需要集中式的使用者管理,可能嚴重妨礙擴展性。

以下先以 NFS 為例看傳統做法,再討論替代方案。

NFS 的安全#

NFS 的基本理念是把遠端檔案系統呈現得像本地檔案系統,因此其安全重點自然放在主從之間的通訊:安全通訊意味著在兩者間建立安全通道(secure channel)。除了安全 RPC,還必須控管檔案存取——由 NFS 的存取控制檔案屬性處理,檔案伺服器負責驗證用戶端的存取權。兩者合起來構成 NFS 的安全架構。

圖 11-28:NFS 的安全架構。

安全 RPC#

NFS 疊在 RPC 系統之上,建立安全通道等於建立安全 RPC。在 NFSv4 之前,安全 RPC 只顧及認證,共有三種方式:

  • 系統認證(system authentication):使用最廣,但其實幾乎沒做認證。這種 UNIX 式方法中,用戶端把有效使用者 ID、群組 ID 及自稱所屬的群組清單,以未簽署的明文傳給伺服器——伺服器完全無從驗證這些識別碼是否真屬於發送者,本質上是假設用戶端已通過正規登入程序、用戶端機器可信。
  • Diffie-Hellman 金鑰交換:用來建立作業階段金鑰(session key),形成所謂 secure NFS。比系統認證好得多,但較複雜,因此實作較少。Diffie-Hellman 可視為公鑰密碼系統;起初沒有安全散布伺服器公鑰的辦法,後來以安全名稱服務補正。長期為人詬病的是公鑰太短——NFS 只用 192 位元,已被證明破解如此短鑰的 Diffie-Hellman 系統近乎輕而易舉。
  • Kerberos:第三種認證協定。

NFSv4 引入 RPCSEC_GSS 後安全性大幅強化。RPCSEC_GSS 是通用安全框架,可支援各式各樣建立安全通道的安全機制:不只提供接入不同認證系統的掛鉤,還支援訊息完整性機密性——舊版 NFS 都不支援這兩項。RPCSEC_GSS 疊在標準安全服務介面 GSS-API 之上。NFSv4 要求 RPCSEC_GSS 配置 Kerberos V5 支援,並且必須支援 LIPKEY——一種公鑰系統,讓用戶端以密碼認證、伺服器以公鑰認證。

圖 11-29:NFSv4 中的安全 RPC。

NFS 安全 RPC 的關鍵在於設計者不自創安全機制,只提供標準化的接入方式。因此像 Kerberos 這樣經過驗證的機制可以直接納入 NFS 實作而不影響系統其他部分;既有機制若被發現有缺陷(如短金鑰的 Diffie-Hellman),也能輕易替換。

RPCSEC_GSS 實作在 NFS 協定底下的 RPC 層,因此也能用於較舊版本的 NFS;只是這項 RPC 層的調整要到 NFSv4 推出才出現。

存取控制#

NFS 的授權與安全 RPC 類似:提供機制、不規定政策。存取控制透過 ACL 檔案屬性支援:一串存取控制項目,每項指定特定使用者或群組的存取權。NFS 區分的操作多半直觀,包括讀、寫、執行檔案、操作檔案屬性、列目錄等;值得一提的是 synchronize 操作,其本質是說明與伺服器同機(colocated)的行程能否繞過 NFS 協定直接存取檔案以提升效能。

NFS 的存取控制模型語意比多數 UNIX 模型豐富得多,原因是 NFS 必須能與 Windows 系統互通——把 UNIX 存取控制模型套進 Windows 的模型,比反過來容易得多。另一項差異是可對多個不同使用者與群組分別指定存取權:傳統上檔案存取只針對單一使用者(擁有者)、單一群組(如專案成員)與其他所有人指定;NFS 則區分許多種類的使用者與行程。

圖 11-30:NFS 在存取控制上所區分的各種使用者與行程。

去中心化認證#

NFS 這類系統的主要問題之一:要妥善處理認證,使用者必須透過中央系統管理註冊。**Secure File Systems(SFS)**搭配去中心化的認證伺服器提供了解法。基本想法很簡單:其他系統缺少的,是讓使用者能指定「某遠端使用者對我的檔案有某些權限」的能力——幾乎所有系統都要求使用者被所有認證伺服器全域知曉。更簡單的做法是讓 Alice 指定「Bob——其詳細資料可在 X 找到——擁有某些權限」,處理 Alice 憑證的認證伺服器再去聯絡伺服器 X 取得 Bob 的資訊。

要解決的重要問題:Alice 的伺服器如何確定它面對的是 Bob 的認證伺服器?答案是 SFS 引入的自我認證名稱(self-certifying name),其目標是把金鑰管理與檔案系統安全分離

SFS 的組織#

為確保跨機器的可攜性,SFS 與多個 NFSv3 元件整合。用戶端機器上有三個元件(不含使用者程式):NFS client 作為使用者程式的介面,與 SFS client 交換資訊——對 NFS client 而言,SFS client 看起來就是另一台 NFS 伺服器。SFS client 負責與 SFS 伺服器建立安全通道,並與本地的 **SFS 使用者代理(user agent)**溝通;agent 是自動處理使用者認證的程式。SFS 不規定使用者認證如何進行——秉持其設計目標,SFS 把這類事務分離出去,不同的使用者認證協定用不同的 agent。

伺服器端同樣有三個元件:NFS server(同樣為了可攜性)、與之溝通的 SFS server(對 NFS server 而言是一個 NFS client),以及獨立的認證伺服器。SFS server 是 SFS 的核心行程,負責處理來自 SFS client 的檔案請求;與 SFS agent 類似,SFS server 與獨立的認證伺服器溝通以處理使用者認證。

圖 11-31:SFS 的組織架構。

自我認證路徑名稱#

SFS 與其他分散式檔案系統最大的不同在其名稱空間的組織:SFS 提供以目錄 /sfs 為根的全域名稱空間,用戶端允許使用者在其中建立符號連結。更重要的是,SFS 用**自我認證路徑名稱(self-certifying pathname)**為檔案命名——路徑名稱本身就攜帶認證「提供該檔案的 SFS 伺服器」所需的全部資訊。它由三部分組成:

  • 位置 LOC:識別 SFS 伺服器的 DNS 網域名稱,或其對應 IP 位址。
  • 主機識別碼 HID:SFS 假設每台伺服器 S 都有公鑰 K_S;HID 是對伺服器位置與其公鑰取密碼學雜湊 H 算出,以 32 位數的 32 進位數字表示。
  • 本地路徑名稱:檔案在 SFS 伺服器上實際存放的路徑。

圖 11-32:SFS 中的自我認證路徑名稱。

用戶端存取 SFS 伺服器時,只要向它索取公鑰,用眾所周知的雜湊函數 H 算出 HID,再與路徑名稱中的值核對——兩者相符,用戶端就知道自己正與名稱中位置所指的那台伺服器對話。

這如何分離金鑰管理與檔案系統安全?SFS 解決的是:取得伺服器公鑰可以完全獨立於檔案系統安全議題。取得金鑰的一種方式是如上所述向伺服器索取;但也可以在本地儲存一批金鑰(例如由系統管理者維護),解析路徑名稱時就在本地查出伺服器金鑰,再用位置部分驗證 HID,不必聯絡伺服器。

命名透明性可以用符號連結達成:對含有一長串 HID 的完整 SFS 路徑建立如 /sfs/vucs 的符號連結後,使用者只需用 /sfs/vucs/home/steen/mbox 這樣的路徑;解析時自動展開成完整 SFS 路徑名稱,並用本地找到的公鑰認證對應的 SFS 伺服器。同樣的手法也讓 SFS 能由**憑證機構(certification authority, CA)**支援:CA 自己跑一台 SFS 伺服器並維護指向其擔保的各 SFS 伺服器的符號連結;用戶端裝好指向 CA 伺服器的連結後,透過如 /certsfs/vucs/home/steen/mbox 的路徑存取檔案,就知道所存取檔案伺服器的公鑰已經過該 CA 認證。

回到去中心化認證的問題:現在所有機制都齊備了——Bob 不必註冊在 Alice 的認證伺服器;只要給定名稱,Alice 的伺服器就能聯絡 Bob 的伺服器,而名稱本身已含公鑰,Alice 的伺服器可據此驗證 Bob 伺服器的身分,之後便能接受 Alice 為 Bob 指定的權限。

安全的點對點檔案共享系統#

至此討論的都是相對容易保護的系統:傳統系統用直接的認證與存取控制加上安全通訊,或把傳統認證擴展成完全去中心化的方案。但面對依賴協作的完全去中心化系統——如點對點檔案共享——事情就複雜了。

DHT 系統中的安全查找#

以 DHT 系統而言,我們必須依賴安全查找操作,其本質是安全路由(secure routing):未故障節點查找鍵 k 時,其請求確實被轉發到負責 k 所關聯資料的節點,或存有該資料副本的節點。安全路由要求處理三件事:

  1. 節點識別碼以安全的方式指派。
  2. 路由表被安全地維護。
  3. 查找請求在節點間被安全地轉發。

識別碼指派不安全時的風險:惡意節點可以給自己指派一個 ID,使得針對特定鍵的查找全被導向自己,或沿著它參與的路徑轉發;節點若能串通,整個群體可形成吞噬大量查找請求的巨大「黑洞」。同樣地,單一節點也可能給自己指派許多識別碼——即女巫攻擊(Sybil attack)——造成相同效果。比 Sybil 攻擊更一般化的是日蝕攻擊(eclipse attack):惡意節點控制了某未故障節點夠多的鄰居,使正確節點幾乎無法正常運作。防禦很難;一個合理的方案是限制每個節點的入邊數,讓攻擊者只能有有限個正確節點指向它;為防攻擊者接管正確節點的所有入向連結,出邊數也應受限

這些方案的共同麻煩是:發放節點識別碼需要一個中央權威——這顯然與點對點系統的去中心化本質相牴觸。

路由表的維護:當路由表項目可以用替代節點填入(為網路鄰近性最佳化時常見),攻擊者很容易說服節點指向惡意節點。填表約束很強的系統(如 Chord)沒有這個問題。解法是把「可選替代」與較受約束的填表方式混合

訊息轉發攻擊的防禦則相對簡單:節點沿多條路徑轉發訊息即可,一種做法是從不同的來源節點發起查找。

安全的協作儲存#

節點被要求協作這件事本身還會引入更多問題。例如協作可能規定:節點提供的儲存量應與它使用別人的量相當——強制執行這種政策相當棘手。一種解法是如 Samsara 系統採用的安全儲存交易(secure trading of storage)

想法很簡單:伺服器 P 想把檔案 f 存到伺服器 Q 時,P 必須騰出與 f 等大的儲存空間專門保留給 Q——Q 於是在 P 那裡持有一筆未償的儲存索取權(claim)

  • 每個參與者保留一塊儲存空間並切成等大的 chunk,每個 chunk 由不可壓縮的資料構成:chunk c_i 是對祕密通關密語 W 串接編號 i 算出的 160 位元雜湊值 h_i。以每單位 256 位元組發放 claim 為例:第一筆 claim 取前 12 個 chunk 加上第 13 個 chunk 的前 16 位元組,串接後以私鑰 K 加密;一般而言第 j 筆 claim 依此類推(起點 k = j×13)。P 要使用 Q 的儲存時,Q 回給 P 一組 claim,P 從此被迫保存它們;Q 自己當然不必存這些 claim——需要時算得出來。
  • 竅門在於:Q 可以不時抽查 P 是否仍保存著它的 claim;P 若無法證明,Q 就可以直接丟棄 P 的資料。讓 P 把 claim 副本傳回去證明太浪費頻寬;假設 Q 發給 P 的是 claim c*{j1}, …, c*{jk},Q 改為傳一個 160 位元字串 d 給 P,要求它算 d 串接 c*{j1} 的 160 位元雜湊 d_1,再把 d_1 串接 c*{j2} 算出 d_2,依此類推——最後 P 只需回傳 d_n 就能證明所有 claim 都還在手上。
延伸案例:claim 的轉存與連帶懲罰

Q 也可能想把自己的檔案複製到另一節點 R,因而必須為 R 持有 claim。若 Q 儲存空間吃緊、但在 P 那裡有已索取的空間,它可以把這些 claim 轉存給 P:假設 P 為 Q 持有 claim C_Q,而 Q 應為 R 持有 claim C_R——既然 Q 能在 P 存放任何東西,它大可把 C_R 存到 P。之後 R 要查驗 Q 是否仍持有其 claim 時,把值 d 交給 Q;Q 把 d 轉給 P、請 P 算雜湊、再把結果回給 R。若 P 其實已不再保存該 claim,Q 會被 R 懲罰,而 Q 反過來可以移除 P 存放的資料作為對 P 的懲罰。