数据库密码放在配置中心里安全吗?改了密码旧密码还会留在哪?信封加密到底省了什么?密钥怎么轮换才不会让数据读不出来?密钥给进程用环境变量还是文件?这篇用 Nacos 3.2.4 和几十行 AES-GCM 代码逐项量出来。
取回:plain-db → 'password=MARKA_7f3a91';cipher-aes-db → 'password=MARKB_9c1d44'
发行版 plugins 目录里名字带 encrypt / cipher / kms 的文件:没有
时间点 | 搜索的标记 | 数据目录(data/) | 日志目录(logs/)
-------+--------------+----------------------------------------+------------
发布之后 | MARKA_7f3a91 | 2 个文件 / 4 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
发布之后 | MARKB_9c1d44 | 2 个文件 / 4 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
改成新值之后 | MARKA_7f3a91 | 1 个文件 / 6 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
改成新值之后 | NEWA_55e2c8 | 2 个文件 / 3 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
改成新值之后 | MARKB_9c1d44 | 1 个文件 / 6 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
改成新值之后 | NEWB_0b7a3f | 2 个文件 / 3 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
删除配置之后 | MARKA_7f3a91 | 1 个文件 / 6 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
删除配置之后 | NEWA_55e2c8 | 1 个文件 / 3 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
删除配置之后 | MARKB_9c1d44 | 1 个文件 / 6 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
删除配置之后 | NEWB_0b7a3f | 1 个文件 / 3 次(如 derby-data/log/log1.dat) | 0 个文件 / 0 次
改成新值之后,plain-db 的历史:[('U', 'password=MARKA_7f3a91'), ('I', 'password=MARKA_7f3a91')];cipher-aes-db 的历史:[('U', 'password=MARKB_9c1d44'), ('I', 'password=MARKB_9c1d44')]
删除之后取 plain-db:(HTTP 404)
搜索的是文件里的原始字节,发布之后约 1 s 就搜了,当时标记已经在 derby-data/log/ 下的文件里;发行版里没有加密插件时,cipher- 前缀的 dataId 和普通 dataId 一样是明文存的没有加密插件时,密码以明文存在数据目录和历史里;改密码、删配置都不会让旧密码消失。
数据 | 方案 | 轮换主密钥要处理的记录数 | 要解密再加密的字节数(DEK 按 32 字节算) | 中位用时(ms) ------------------+---------------------+--------------+--------------------------+--------- (a)10000 条 × 64 B | A 单密钥(全部重新加密) | 10000 | 640,000 | 17.5 (a)10000 条 × 64 B | B 每条一个 DEK(重新包 DEK) | 10000 | 320,000 | 17.2 (a)10000 条 × 64 B | C 每 20 条共用一个 DEK | 500 | 16,000 | 0.84 (b)100 条 × 1 MiB | A 单密钥(全部重新加密) | 100 | 104,857,600 | 33.7 (b)100 条 × 1 MiB | B 每条一个 DEK(重新包 DEK) | 100 | 3,200 | 0.2 (b)100 条 × 1 MiB | C 每 20 条共用一个 DEK | 5 | 160 | 0.01 单密钥轮换要把所有数据读出来再写回去;信封加密轮换主密钥只处理 DEK,与数据本身多大无关。轮换 DEK 本身(也就是换掉保护数据的那把密钥)仍然要重新加密对应的数据
轮换主密钥只需重新包数据密钥,和数据多大无关;轮换数据密钥仍要重新加密数据。
正确顺序: 阶段 | K1 记录数 | K2 记录数 | 读取方认识的密钥 | 读不出来的记录数 ----------------------+--------+--------+--------------+--------- 开始(都是 K1) | 1000 | 0 | ['K1'] | 0 读取方认识 K1 和 K2 | 1000 | 0 | ['K1', 'K2'] | 0 写入方改用 K2,又写了 400 条新记录 | 1000 | 400 | ['K1', 'K2'] | 0 后台把 K1 记录重新加密成 K2 | 0 | 1400 | ['K1', 'K2'] | 0 读取方不再认识 K1 | 0 | 1400 | ['K2'] | 0 出错一:写入方先改用 K2: 阶段 | K1 记录数 | K2 记录数 | 读取方认识的密钥 | 读不出来的记录数 --------------------------------+--------+--------+--------------+--------- 开始(都是 K1) | 1000 | 0 | ['K1'] | 0 写入方先改用 K2(读取方还只认识 K1),又写了 400 条 | 1000 | 400 | ['K1'] | 400 读取方后来也认识了 K2 | 1000 | 400 | ['K1', 'K2'] | 0 出错二:重新加密之前忘掉 K1: 阶段 | K1 记录数 | K2 记录数 | 读取方认识的密钥 | 读不出来的记录数 ------------------+--------+--------+----------+--------- 开始(都是 K1) | 1000 | 0 | ['K1'] | 0 还没重新加密,读取方就忘掉了 K1 | 1000 | 400 | ['K2'] | 1000
先让读取方认识新密钥,再切写入,再重新加密旧数据,最后才让读取方忘掉旧密钥。
标记(交给进程的方式) | ps 的输出 | 结果
------------------+----------------+----
ARGSECRET_a93f10 | ps -o command= | 能搜到
ENVSECRET_6be2a8 | ps -o command= | 搜不到
FILESECRET_d41c7e | ps -o command= | 搜不到
ARGSECRET_a93f10 | ps eww | 能搜到
ENVSECRET_6be2a8 | ps eww | 能搜到
FILESECRET_d41c7e | ps eww | 搜不到
文件权限:0o600。命令行参数用普通的 ps 就能看到,环境变量在 ps eww 里能看到(这里都是同一个用户,别的用户能不能看到我没有测);文件里的内容 ps 看不到,由文件权限保护
ERRORS {}命令行参数和环境变量在 ps 里能看到,文件靠权限保护。