上个月跑等保,客户那边管安全的人冷不丁问了我一句:数据库文件要是被人整个端走,你们怎么办?
我愣了一下。
倒不是没想过,而是突然发现平时聊数据库安全,翻来覆去就是账号、权限、防火墙这些"看门口"的活儿。真到硬盘、备份磁带离开机房那一步------文件里的东西全是明文,谁来都看得懂。这问题一下把我问住了。
于是我把金仓官方知识库里"安全与权限"分类下的数据加密文档翻了个遍,六篇,从传输一路讲到代码加密。光看不过瘾,又拉了台虚拟机一条条跑。本文就是那次折腾的流水账,坑也没少踩,一并写进来。
四层楼,各管各的
先说整体感受。KingbaseES 的加密能力,不是一锅炖,更像一栋楼分四层住着四户人。
一层管传输。SSL/TLS,管的是报文在路上跑的时候别被人偷看。二层三层管存储,一个粗一个细------表空间加密是粗网眼的渔网,一捞一大片;到了字段级别,就换细筛子,kbcrypto 和全密态都是干这个的。四层最特别,它不管数据,管代码:数据库里那些函数、存储过程的源码,也有专门的锁。
四户互不干扰。你按合规要求挑着用就行。

一层:SSL/TLS
明文传输这个事,抓过一次包就忘不掉。客户端连数据库,报文里都是赤裸裸的业务数据,中间人看得一清二楚。
SSL,还有它的继任者 TLS,干的就是给报文穿上衣服的活。KES 对这套的支持挺全,尤其双向认证做得细------这词儿值得停下来说说。
单向认证,是客户端验服务器:"你真是那台服务器吗?"双向认证呢,服务器反手也验一把客户端:"你手里有证书吗?没有?门都没有。"也就是说密码被盗还不够,攻击者还得连证书一起偷。
再加上等保之类法规对传输加密有硬性要求。这事没什么好犹豫的,做。
配置的起点是证书。官方实验从搭 rootCA 开始,我照着跑,命令没几行:
bash
# 根 CA 的私钥,2048 位 rsa
openssl genrsa 2048 > rootca.key
# 自签名根证书。交互式提问,一路回车即可
openssl req -new -x509 -nodes -days 3600 -key rootca.key -out rootca.crt


根证书有了,该 KES 服务器出场。生成它的私钥和证书请求文件:
bash
openssl req -newkey rsa:2048 -nodes -keyout kesserver.key -out kesserver.csr

说到这我必须交代一个自己踩的坑。生成 CSR 时会问你 Common Name,我当时随手填了个主机别名。心想反正测试环境嘛。
结果客户端拿 verify-full 模式一连,校验失败。
查了才知道,这个字段会被拿来跟服务器名字对号。要么填服务器真名,要么填 * 通配。之后证书发到服务端、客户端两边,通道一开,传输这块就算齐活了。
二层:表空间加密
现在回答开头那个问题:文件被拷走怎么办?
透明存储加密。具体到实现,是 sysencrypt 这个插件。
原理一句话:内存里永远明文,落盘那一刻加密,读回内存那一刻解密。你在内存里查表、改数据、建索引,跟普通表一模一样,业务代码一个字不用动。页面为单位做块加密,开销压在存储层,性能损失不大------至少官方文档是这么说的,我实测下来也确实感知不明显。
密钥怎么管?靠一个叫 TDE 钱包的东西。wallet 目录藏在集簇数据目录里,跟数据文件分开放。三级密钥:主密钥一把,全局唯一,管着下面的对象密钥;每个加密对象一把对象密钥;最后真正动手加密数据页的,是从对象密钥加块标识派生的块级密钥。
算法给了 SM4 和 RC4 两个。做密评就别犹豫,SM4,国密,128 位分组 128 位密钥。

开干前先装插件。kingbase.conf 里把 sysencrypt 加到 shared_preload_libraries,重启,建扩展:
sql
shared_preload_libraries = '...,sysencrypt'
-- 重启后
CREATE EXTENSION sysencrypt;
然后是钱包。第一次用要设密码:
sql
ALTER WALLET WITH PASSWORD "Sm4@Wallet#2026";
OPENUP WALLET WITH PASSWORD "Sm4@Wallet#2026";
打住,这里插一句很重要的话。钱包密码,忘了就是忘了。没有找回功能。钱包一丢,加密数据永远是一堆乱码。这密码请多备份几处------写进你的密码管理器、打印一份锁抽屉里,都行。
钱包开了,建加密表空间。两条路。
手动派:建的时候自己给密钥,掌控感强。
sql
CREATE TABLESPACE tbs_finance_enc LOCATION '/home/kingbase/tbs_finance'
WITH (encryption = true, enckey = 'Sm4@Fin#2026');
enckey 上限 16 字节,超了截断。里头至少一个数字一个字母,单引号包着。不给也行,系统替你生成。
偷懒派:把 sysencrypt.encrypt_user_tablespace 参数一开(默认 off),之后新建的用户表空间全部自动加密,encryption = true 都省了。
哦对了,这个参数关掉不影响存量------已经加密的表空间照样加密,退不回明文,除非删了重建。这点设计我觉得挺合理,省得手一抖把生产库加密关了。
加密有没有生效,别信感觉,翻磁盘最实在。加密表空间里建表、插几行、CHECKPOINT 落盘,SELECT sys_relation_filepath('表名'); 拿到物理路径,hexdump 一敲------满屏乱码。换默认表空间再来一遍?hexdump 里明文字符串清清楚楚。这一对比,比任何文档都有说服力。
限制有三条,记一下:系统表空间和默认表空间不让加密,只有自建的可以;表空间加密跟表级加密互斥,二选一;不过字段类型、约束、索引它都不挑,这点倒是省心。
三层:刀法由粗到细
撒网式的表空间加密有个问题------太浪费。一张几百万行的用户表,真正敏感的也许就身份证那一列,为了它把整表都加密,杀鸡用牛刀。
所以还有三把更细的刀。一把比一把狠。
第一把:sysencrypt,整表加密
跟表空间加密同一个插件、同一套原理,只是范围缩到单表。用法简单得出奇,建表语句尾巴上加个词:
sql
CREATE TABLE customer_encrypted (
id INT PRIMARY KEY,
name VARCHAR(50),
id_card VARCHAR(18),
phone VARCHAR(11),
created_at TIMESTAMP DEFAULT NOW()
) ENCRYPTED;
增删改查照旧。想知道表加密了没,问数据库:
sql
SELECT sysencrypt.is_table_encrypted('customer_encrypted');
SELECT * FROM sysencrypt.sys_table_encrypt WHERE tablename = 'customer_encrypted';
hexdump 验证法照旧适用。
但切换加密状态这件事,我劝你慎重。ALTER TABLE ... ENCRYPTED(或者反向的 NOT ENCRYPTED)不是改个开关那么轻巧。数据库背后干的事是把整张表重建一遍:先拿排他锁,然后开新数据文件,一行一行读出来、加密或解密、写回去,改系统目录,删旧文件。做完之后 sys_relation_filepath 给出的路径全变了------重建的痕迹。
代价呢?操作期间整表锁死,谁也别想读写。新旧文件短暂并存,磁盘得腾出一块等于原表大小的地方。大表跑起来又慢,WAL 还哗哗地生成。所以------维护窗口,测试环境先演练,一样都不能省。
第二把:kbcrypto,字段级国密
整表加密还嫌粗?上 kbcrypto。
这插件默认就躺在 shared_preload_libraries 里,建个扩展直接用。它给你的是一组显式调用的函数:
| 函数 | 干什么用 |
|---|---|
| sm4(data, key, mode) | SM4 加解密。mode 填 0 加密、1 解密,参数是 BYTEA |
| sm4_ex(data, key, mode, pad) | 多个填充模式:1 按 16 字节倍数强制填,0 非强制填 0x0 |
| sm3(data) | SM3 摘要,不可逆 |
| rc4(data, key, mode) | RC4。用不用看你们的安全规范 |
思路是应用自己说了算:写入时加密,读取时解密。身份证号、手机号这种字段,密文直接用 BYTEA 存:
sql
CREATE TABLE staff (
id INT PRIMARY KEY,
name VARCHAR(50),
id_card_encrypted BYTEA,
phone_encrypted BYTEA
);
INSERT INTO staff VALUES (
1, '王五',
sm4('11010119900307663X'::BYTEA, '0123456789ABCDEF'::BYTEA, 0),
sm4('13912345678'::BYTEA, '0123456789ABCDEF'::BYTEA, 0)
);
SELECT id, name,
convert_from(sm4(id_card_encrypted, '0123456789ABCDEF'::BYTEA, 1), 'UTF8') AS id_card,
convert_from(sm4(phone_encrypted, '0123456789ABCDEF'::BYTEA, 1), 'UTF8') AS phone
FROM staff;
小坑记录:这批函数吐的是二进制。终端显示成啥样看 bytea_output 参数,默认 hex,一串 \x 开头的十六进制看着眼晕。SET bytea_output TO escape; 换转义格式。解密结果拿 convert_from 转回文本。还有,密文字段建表就用 BYTEA,别 TEXT 存完再折腾转换。
密码字段走另一条路。密码不该可逆------SM3 出摘要存起来,登录时把输入同样摘要一遍、比对。讲究点还得加盐加迭代。裸哈希,高手面前撑不了几个回合。
第三把:kdb_ce 全密态
前两把刀有个共同的软肋:数据到了内存、进了查询执行,终究是明文。DBA 想看,拦不住。
金融核心账户、医疗病历、高密级政务数据------这些场景的要求更过分:连管理员都不许碰明文。
kdb_ce 就是干这个的。名字叫全密态计算,存储、传输、处理全程密文,还能在密文上做一定范围的条件查询。
怎么做到的?双层密钥。客户端主密钥 CMK 放在你自己的本地 KMS 里,它保护列加密密钥 CEK,CEK 再加密列数据。数据库端只见过被包了一层的密钥材料和密文。明文密钥压根不出客户端。DBA 把库翻个底朝天,也全是乱码。

配置比前两个麻烦些。插件照挂 shared_preload_libraries、重启、建扩展之外,还得设 LOCALKMS 环境变量、建密钥目录。另外客户端连接必须带 -M 参数进全密态模式------整条链路的关键开关,少了它一切白搭:
sql
CREATE CLIENT MASTER KEY cmk_1 WITH (
KEY_STORE = LOCALKMS, KEY_PATH = "kms_1", ALGORITHM = RSA_2048);
CREATE COLUMN ENCRYPTION KEY cek_1 WITH VALUES (
CLIENT_MASTER_KEY = cmk_1, ALGORITHM = AEAD_AES_256_CBC_HMAC_SHA256);
CREATE TABLE creditcard_info (
id_number INT,
name TEXT CLENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = cek_1,
ENCRYPTION_TYPE = DETERMINISTIC),
credit_card VARCHAR(19) CLENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = cek_1,
ENCRYPTION_TYPE = DETERMINISTIC)
);
CLENCRYPTED 声明哪列加密。加密类型二选一,得想清楚:DETERMINISTIC,确定性加密,同明文永远同密文,能做等值查询、能 JOIN,代价是密文会泄露"哪些行值相同"这个规律;RANDOMIZED,同明文给不同密文,更安全,但等值匹配没戏。要进 WHERE 条件的列用前者,只存不查的用后者。
验证挺有意思。换个不带 -M 的普通连接查这张表------加密列全是乱码。翻数据文件,乱码。抓包,还是乱码。名不虚传。
代价也得认:部署、运维、性能,三头都要花钱。给最核心的表用就好,全库铺开没必要。
四层:锁代码
数据护住了,还有个东西容易漏------代码本身。
存储过程、函数里写的都是业务逻辑。计费规则、风控口径,全在系统目录里明晃晃摆着,谁都能 \sf 一下看全文。合适吗?未必。
KingbaseES 拿系统自带的 DBMS_DDL 插件解决这事,不用额外安装。
函数就两个。WRAP 只管加密:把你的 CREATE 语句吞进去,吐回来一段加密文本,至于执不执行,你自己定。另一个 CREATE_WRAPPED 更省心,加密加创建一次做完,实操里大家基本都用它:
sql
CALL dbms_ddl.CREATE_WRAPPED(q'[
CREATE PROCEDURE getValue(pid int) AS
BEGIN
select now();
RAISE NOTICE 'pid=%',pid;
END ;
]');
完了 \sf getValue 看一眼------函数体变成了 AS WRAPPED 加一长串 Base64 密文。逻辑一个字看不见。但调用一切正常,CALL getValue(110); 该输出输出。函数同理。
怎么选
四户人的差异,粗粗一比就出来了。表空间加密管一大片,sysencrypt 收缩到一张表,kbcrypto 再收缩到一个字段,kdb_ce 管得最绝------连 DBA 都别想看明文。改造量从零一路涨到要动 SQL,性能代价也是全密态最贵。国密这边,前三家都带 SM4,kbcrypto 还能显式调 SM3;全密态的示例走的是 AES-256 那套。
选型我自己只归纳三条,都很土。
头一条,想明白你要防的是谁。怕硬盘、备份流出去?透明加密就行,表空间级表级随你。怕的就几个字段泄露?kbcrypto。要是连 DBA 都在你的防备名单里,那没得挑,kdb_ce。
第二条,算账。老系统要低成本上线,透明加密几乎零改造,这笔账怎么算都划算。
第三条,听测评方的。等保密评场合国密优先,SM4、SM3 先用上;最终口径以测评方为准,自己拍脑袋定了也过不了审。
最后
折腾一圈,我印象最深的反倒是另一件事:这套体系每层都给你留了验货的口子。传输层可以抓包,存储层直接 hexdump,全密态拿不带 -M 的连接一查便知,函数加密 \sf 一下就能看到密文。做安全不能凭感觉,一层层亲手验过,心里才有底。
回头再答开头那个问题,就一句话的事:加密表空间加钱包。硬盘上躺着的全是密文,被人端走也无非一堆乱码。要是 DBA 也在防备名单里,那就再叠一层 kdb_ce。