数据库

Vault

在 Postgres 中管理密钥。


Vault 是一个 Postgres 扩展及配套的 Supabase 用户界面,能够安全便捷地在数据库中存储加密的密钥和其他数据。这为 Postgres 的使用开辟了许多可能性,超越了标准发行版的功能。

在底层,Vault 是一个 Secrets 表,其中的数据使用认证加密方式存储在磁盘上。这些数据通过 Postgres 视图以解密形式提供,使得应用程序可以通过 SQL 使用这些密钥。由于密钥以加密和认证形式存储在磁盘上,任何备份或复制流也会保留这种加密方式,且无法被解密或伪造。

Supabase 为 Vault 提供了一个仪表盘界面,使密钥存储变得简单。只需点击按钮、输入密钥并保存即可。

您可以使用 Vault 存储各种密钥——从环境变量到 API 密钥。然后可以在数据库的任何地方使用这些密钥:Postgres 函数、触发器以及Webhooks。从 SQL 的角度来看,访问密钥就像查询表(在这里是视图)一样简单。底层的密钥表将以加密形式存储。

使用 Vault

您可以通过用户界面或 SQL 来管理密钥。

添加密钥

还有一个便捷的创建密钥函数 vault.create_secret()

1
select vault.create_secret('my_s3kre3t');

该函数会返回新密钥的UUID。

显示结果
1
2
-[ RECORD 1 ]-+-------------------------------------create_secret | c9b00867-ca8b-44fc-a81d-d20b8169be17

密钥还可以设置可选的_唯一_名称和描述。这些也是 vault.create_secret() 的参数:

1
select vault.create_secret('another_s3kre3t', 'unique_name', 'This is the description');
显示结果
1
2
3
4
5
6
7
8
9
-[ RECORD 1 ]-----------------------------------------------------------------id | 7095d222-efe5-4cd5-b5c6-5755b451e223name | unique_namedescription | This is the descriptionsecret | 3mMeOcoG84a5F2uOfy2ugWYDp9sdxvCTmi6kTeT97bvA8rCEsG5DWWZtTU8VVeE=key_id |nonce | \x9f2d60954ba5eb566445736e0760b0e3created_at | 2022-12-14 02:34:23.85159+00updated_at | 2022-12-14 02:34:23.85159+00

查看密钥

当您查看 vault.secrets 表时,会发现数据是以加密形式存储的。为了解密数据,系统自动创建了一个视图 vault.decrypted_secrets。该视图会实时解密密钥数据:

1
2
3
4
select * from vault.decrypted_secrets order by created_at desc limit 3;
显示结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-[ RECORD 1 ]----+-----------------------------------------------------------------id | 7095d222-efe5-4cd5-b5c6-5755b451e223name | unique_namedescription | This is the descriptionsecret | 3mMeOcoG84a5F2uOfy2ugWYDp9sdxvCTmi6kTeT97bvA8rCEsG5DWWZtTU8VVeE=decrypted_secret | another_s3kre3tkey_id |nonce | \x9f2d60954ba5eb566445736e0760b0e3created_at | 2022-12-14 02:34:23.85159+00updated_at | 2022-12-14 02:34:23.85159+00-[ RECORD 2 ]----+-----------------------------------------------------------------id | c9b00867-ca8b-44fc-a81d-d20b8169be17name |description |secret | a1CE4vXwQ53+N9bllJj1D7fasm59ykohjb7K90PPsRFUd9IbBdxIGZNoSQLIXl4=decrypted_secret | another_s3kre3tkey_id |nonce | \x1d3b2761548c4efb2d29ca11d44aa22fcreated_at | 2022-12-14 02:32:50.58921+00updated_at | 2022-12-14 02:32:50.58921+00-[ RECORD 3 ]----+-----------------------------------------------------------------id | d91596b8-1047-446c-b9c0-66d98af6d001name |description |secret | S02eXS9BBY+kE3r621IS8beAytEEtj+dDHjs9/0AoMy7HTbog+ylxcS22A==decrypted_secret | s3kre3t_k3ykey_id |nonce | \x3aa2e92f9808e496aa4163a59304b895created_at | 2022-12-14 02:29:21.3625+00updated_at | 2022-12-14 02:29:21.3625+00

请注意该视图包含一个 decrypted_secret 列,其中存储着解密后的密钥。视图不会持久化存储在磁盘上,仅在查询时执行,因此密钥在磁盘上、备份文件或复制流中始终保持加密状态。

您必须始终通过适当的SQL权限设置来保护对该视图的访问,因为任何能访问该视图的人都能获取解密后的密钥。

更新密钥

可以使用 vault.update_secret() 函数来更新密钥,该函数使密钥更新变得简单,只需将密钥 UUID 作为第一个参数,然后提供更新后的密钥、可选的唯一名称或描述:

1
2
3
4
5
6
7
select vault.update_secret( '7095d222-efe5-4cd5-b5c6-5755b451e223', 'n3w_upd@ted_s3kret', 'updated_unique_name', 'This is the updated description' );
显示结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-[ RECORD 1 ]-+-update_secret |postgres=> select * from vault.decrypted_secrets where id = '7095d222-efe5-4cd5-b5c6-5755b451e223';-[ RECORD 1 ]----+---------------------------------------------------------------------id | 7095d222-efe5-4cd5-b5c6-5755b451e223name | updated_unique_namedescription | This is the updated descriptionsecret | lhb3HBFxF+qJzp/HHCwhjl4QFb5dYDsIQEm35DaZQOovdkgp2iy6UMufTKJGH4ThMrU=decrypted_secret | n3w_upd@ted_s3kretkey_id |nonce | \x9f2d60954ba5eb566445736e0760b0e3created_at | 2022-12-14 02:34:23.85159+00updated_at | 2022-12-14 02:51:13.938396+00

深入探究

如前所述,Vault 使用透明列加密(TCE)以认证加密形式存储密钥。您可能对其中的一些细节感到好奇。认证是什么意思?加密密钥存储在哪里?本节将解释这些细节。

带关联数据的认证加密

TCE 的首要重要特性是它采用了带关联数据的认证加密算法(基于 libsodium)。

加密密钥位置

认证加密意味着除了数据被加密外,还会进行签名以防止伪造。您可以确保数据是由您信任的人加密的,这是单纯加密无法提供的保障。解密函数会在_解密值之前_验证签名是否有效。

关联数据意味着您可以将同一行的其他列包含在签名计算中。这并不会加密那些其他列,而是确保您的加密值仅与该行的列相关联。如果攻击者将加密值从其他行复制到当前行,签名将被拒绝(前提是您在关联数据中使用了唯一列)。

另一个重要特性是加密密钥永远不会与加密数据一起存储在数据库中。即使攻击者获取了您整个数据库的转储,他们也只会看到加密数据,而永远不会看到加密密钥本身

这是一个重要的安全预防措施——将加密密钥存储在数据库本身几乎没有价值,这就像锁上前门却把钥匙留在锁里一样!将密钥存储在数据库外部解决了这个问题。

密钥存储在哪里?Supabase 在我们的安全后端系统中创建并管理加密密钥。我们将此密钥与您的数据分开保存,确保其安全。您仍然可以控制您的密钥——我们提供了一个单独的 API 端点,如果您想在 Supabase 之外解密数据,可以使用该端点访问密钥。

应仔细考虑哪些角色应该有权访问 vault.secrets 表。有两种授予访问权限的方式:第一种是 postgres 用户可以显式授予对保险库表本身的访问权限。

相关资源