Linux服务器下,如何安全存储DEK以让指定进程访问数据库加密数据?
你的这个需求很贴合实际场景——用KEK解锁DEK,再让特定进程(自研应用、Nginx脚本)安全访问DEK,同时防止其他进程碰它。下面我给你几个可落地的方案,从简单到灵活,你可以根据自己的技术栈选择:
方案一:基于文件系统权限的临时存储(最易实现)
这个方案利用Linux的用户组和权限机制,把明文DEK存到内存文件系统(/run是tmpfs,重启就消失,不会落到磁盘),只给指定进程的运行用户开放读取权限:
创建专属用户组
先建一个专门用来控制DEK访问的用户组,比如dek-access,把需要访问DEK的进程运行用户加进去:groupadd dek-access # 把你的应用运行用户加入组 usermod -aG dek-access appuser # 把Nginx的运行用户(通常是www-data)加入组 usermod -aG dek-access www-data准备存储目录
在/run下创建一个目录,设置好权限,防止无关用户看到这个目录:mkdir -p /run/dek chown root:dek-access /run/dek chmod 0750 /run/dek启动时解密DEK并写入文件
系统启动时,让管理员输入KEK,解密存在磁盘上的加密DEK,然后写入权限严格控制的文件:# 假设你之前用OpenSSL加密DEK存到了/etc/dek/encrypted_dek.key openssl enc -aes-256-cbc -d -in /etc/dek/encrypted_dek.key -out /run/dek/secret.key -k $(read -s -p "Enter KEK: " kek && echo $kek) # 设置文件权限:只有root和dek-access组能读 chmod 0640 /run/dek/secret.key chown root:dek-access /run/dek/secret.key注意:不要直接在命令行输入KEK,用
read -s隐藏输入,避免被命令历史记录捕获。进程读取DEK
- 你的自研应用:直接读取
/run/dek/secret.key即可,用完记得从内存中清除(比如用memset覆盖变量内存)。 - Nginx下的脚本(比如PHP):用
file_get_contents('/run/dek/secret.key')读取,用完立即unset变量。
- 你的自研应用:直接读取
方案二:共享内存存储(无磁盘落地,更安全)
如果不想DEK以文件形式存在哪怕是内存文件系统里,可以用Linux的共享内存(Shared Memory),数据完全在内存中,重启后自动消失:
创建共享内存并写入DEK
写一个简单的C程序(或者用现成工具),在解密DEK后创建共享内存区域,设置权限为dek-access组可访问:#include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <grp.h> #include <sys/stat.h> int main() { // 创建共享内存对象,名称自定义 int shm_fd = shm_open("/dek_shared_mem", O_CREAT | O_RDWR, 0640); // 设置共享内存大小(假设DEK是256位,32字节) ftruncate(shm_fd, 32); // 映射到进程内存 char *dek_ptr = mmap(NULL, 32, PROT_WRITE, MAP_SHARED, shm_fd, 0); // 写入解密后的DEK(这里替换成实际的明文DEK) strncpy(dek_ptr, "your_decrypted_dek_value", 32); // 设置共享内存的所属组为dek-access struct group *grp = getgrnam("dek-access"); fchown(shm_fd, getuid(), grp->gr_gid); // 清理资源 munmap(dek_ptr, 32); close(shm_fd); return 0; }进程读取共享内存
你的应用和Nginx脚本可以通过连接这个共享内存对象读取DEK。如果是脚本,可以写一个小的C工具编译成二进制,脚本调用这个工具获取DEK,避免直接处理共享内存的复杂操作。
方案三:守护进程+Unix域套接字(细粒度控制)
如果需要更严格的访问控制(比如只允许特定进程ID访问,或者验证请求合法性),可以用一个守护进程来管理DEK,通过Unix域套接字提供访问:
编写DEK守护进程
守护进程启动时用KEK解密DEK,然后监听一个Unix域套接字,设置套接字权限为dek-access组可访问。当收到指定进程的请求时,返回DEK。
核心逻辑:- 监听套接字:
socket(AF_UNIX, SOCK_STREAM, 0) - 设置套接字权限:
chmod("/run/dek_socket", 0660),chown到root:dek-access - 接受连接后,验证客户端的用户ID(通过
getsockopt获取SO_PEERCRED),确认是dek-access组的用户后返回DEK
- 监听套接字:
进程请求DEK
你的应用和Nginx脚本通过连接这个Unix域套接字,发送请求获取DEK。这种方式可以避免DEK直接暴露,守护进程还可以记录所有访问日志,方便审计。
关键注意事项
- KEK的输入安全:系统启动时,最好用systemd的交互式服务(
Type=oneshot)或者启动界面(比如Plymouth)让管理员输入KEK,不要用普通命令行输入,防止被历史记录或进程列表捕获。 - 加密存储DEK:平时DEK必须以加密状态存在磁盘上,比如用AES-256加密,不要存明文。
- 最小权限原则:确保你的应用和Nginx脚本的运行用户只有必要的权限,比如
appuser只需要读DEK和访问数据库,不要用root运行。 - 内存清除:进程读取DEK后,用完要立即从内存中清除(比如用
memset覆盖变量,脚本中unset变量),防止内存dump泄露DEK。
内容的提问来源于stack exchange,提问作者Lieuwe

