假設您有一個簡單的 Web 應用程式,它在將資料儲存到 SQL 資料庫之前對其進行加密。
一個快速而粗糙的實現可能看起來像這樣:
class User { public function __construct( public readonly string $username, public string $email, public string $fullName ) {} } class UserModel { public function __construct(protected Database $db) {} public function save(User $user): bool { return $this->db->upsert( 'users', [ <font>// set<i> 'full_name' => aes128gcm_encrypt($user->fullName), 'email' => aes128gcm_encrypt($user->email) // encryption details abstracted<i> ], [ // where<i> 'username' => $user->username ] ); } public function fetch(string $username): User { $row = $this->db->fetch('users', ['username' => $username]); return new User( $username, aes128gcm_decrypt($row['email']), aes128gcm_decrypt($row['full_name']) ); } }
|
上述虛擬碼中的抽象aes128gcm函式是一種靜態加密方法:在加密過程中從 KMS 獲取金鑰,並將加密的資料金鑰儲存在密文稍後解密時可以引用的位置。
假設出現這樣一個情況:
Alice 和 Bob 使用同一家健康保險提供商,該提供商儲存了雙方的敏感醫療記錄。Bob 是他和 Alice 共同使用的保險公司的資料庫管理員。
資料都是在 Web 應用程式中加密的,因此 Bob 可以訪問的所有資料都與隨機資料無異。
他可以透過應用程式訪問自己的帳戶並檢視自己的資料,但從資料庫伺服器上的有利位置看不到 Alice 的資料。
這是一個在很多情況下都有效的愚蠢簡單攻擊:
- Bob 複製Alice 的加密資料,並覆蓋資料庫中的記錄,然後訪問保險提供商的網路應用程式。
Web 應用程式能夠解密使用不同金鑰加密的不同記錄。如果您將為 Alice 加密的記錄傳遞給應用程式以解密 Bob 的記錄,並且您沒有驗證您的訪問模式,則 Bob 可以透過執行此攻擊來讀取 Alice 的資料。
執行以下操作來證明這種混淆代理風險:
$model = new UserModel($db); $model->save(new User('alice', 'alice@example.com', 'Alice McWonderland')); $model->save(new User('bob', 'bob@example.com', 'Bob BurgerMeister')); <font>// Fetch Alice's data<i> $aliceData = $db->fetch('users', ['username' => 'alice']); $bobData = $db->fetch('users', ['username' => 'bob']); // This is the attack the database server can perfrom:<i> // Replace Bob's full_name with Alice's email<i> $db->upsert('users', [ 'full_name' => $alice['email'] ], ['username' => 'bob']); $badBob = $model->fetch('bob');
|
現在 Bob 的全名設定為 Alice 的電子郵件地址。
- 現在想象一下有人對工資系統中的工資欄位進行同樣的攻擊。
解決方法
使用 AAD 機制(標準 AEAD 介面的一部分):將密文繫結到其上下文。這可以是客戶 ID、資料庫表主鍵的每一行值,或者其他完全不同的東西。
上個月釋出的CipherSweet 4.7.0 版現在只需要進行如下程式碼更改即可緩解應用程式中的混淆問題:
$multiRowEncryptor = new EncryptedMultiRows($engine); $multiRowEncryptor + ->setAutoBindContext(true) + ->setPrimaryKeyColumn('table2', 'id') ->addTextField('table1', 'field1')
|
這是對新的增強型 AAD功能的補充,它允許基於其他欄位和/或字串文字進行靈活而強大的上下文繫結。
(事實上,這個新的便捷功能實際上在底層使用了增強型 AAD。)
然而,這並不是免費的:使用者必須在寫入記錄之前知道記錄的序列/主鍵,以便在加密欄位時將其用作 AAD。然而,這比期望 PHP 開發人員自己管理上下文繫結的複雜性要容易得多。
鑑於客戶端加密專案普遍使用不安全的分組密碼模式(或ECB,即完全沒有分組密碼模式),因此,它們中的大多數都很難解決混淆代理攻擊。甚至我在 2018 年製作 CipherSweet 時一開始也沒有做對。