靜態加密:存在代理混淆的安全漏洞

banq發表於2024-06-10


假設您有一個簡單的 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 時一開始也沒有做對。

 

相關文章