如何通过管道实现GPG解密后直接加密?避免明文临时文件泄露敏感数据
我太懂你这个需求了——处理敏感数据的时候,最怕明文落地,哪怕是临时文件都可能留隐患。你遇到的问题其实是两个典型的管道操作坑:一是两个GPG进程同时抢标准输入要密码,导致提示叠在一起;二是直接输出到原加密文件会提前截断它,最后解密失败还把原文件搞坏了。
先给你提个最紧急的醒:你原来的命令里直接把加密输出写到sensitive.gpg,这绝对不行!管道右边的GPG进程会立刻打开这个文件并清空它,而左边的GPG进程这时候还没读完原文件的内容,最后不仅解密失败,你原有的加密文件也会被破坏,这个一定要先避开。
下面给你几个靠谱的解决办法,按需选:
方法一:用sponge工具解决文件覆盖问题+密码文件避免输入冲突
sponge是moreutils工具包中的小工具,它会等所有输入都读完之后,再写入目标文件,完美解决管道覆盖原文件的问题。配合密码文件的话,还能让两个GPG进程不用抢输入:
# 先把解密和加密的密码分别写到临时文件(一定要给临时文件加严格权限) echo -n "你的解密密码" > /tmp/decrypt_pass.tmp echo -n "你的加密密码" > /tmp/encrypt_pass.tmp chmod 600 /tmp/*.tmp # 执行解密+加密的管道操作 gpg --no-symkey-cache --pinentry-mode loopback --passphrase-file /tmp/decrypt_pass.tmp --decrypt sensitive.gpg | gpg --encrypt --symmetric --pinentry-mode loopback --passphrase-file /tmp/encrypt_pass.tmp | sponge sensitive.gpg # 用完立刻删掉临时密码文件,别留痕迹 rm -f /tmp/decrypt_pass.tmp /tmp/encrypt_pass.tmp
如果解密和加密用的是同一个密码,那只需要一个密码文件就行,省一步操作。
方法二:手动输入密码但不混乱(不用临时密码文件)
要是你不想把密码写到文件里,想手动输入,那就用文件描述符把两个进程的输入通道分开,让它们各走各的:
# 先提示输入解密密码,再提示加密密码,完全不冲突 gpg --no-symkey-cache --pinentry-mode loopback --passphrase-fd 3 --decrypt sensitive.gpg 3< <(read -s -p "请输入解密密码: " dec_pass; echo "$dec_pass") | gpg --encrypt --symmetric --pinentry-mode loopback --passphrase-fd 4 4< <(read -s -p "请输入加密密码: " enc_pass; echo "$enc_pass") | sponge sensitive.gpg
这里用了文件描述符3和4分别给解密、加密进程传密码,两个提示会依次出现,不会叠在一起,你输入的时候也不会互相干扰。
方法三:用内存临时文件(简单直观)
如果你的系统有tmpfs(大部分Linux发行版的/dev/shm就是内存挂载的文件系统),可以把解密后的内容存在内存里的临时文件,再加密回原文件,全程明文只在内存里,不会写到磁盘:
# 在内存文件系统里创建临时文件 tmp_file=$(mktemp -p /dev/shm) # 先解密到内存临时文件 gpg --no-symkey-cache --pinentry-mode loopback --decrypt sensitive.gpg > "$tmp_file" # 再从内存临时文件加密回原文件 gpg --encrypt --symmetric --pinentry-mode loopback --output sensitive.gpg "$tmp_file" # 用完立刻删掉内存里的临时文件 rm -f "$tmp_file"
这个方法操作起来最直观,不用记复杂的管道参数,适合对命令行不太熟的场景。
最后再给你解释下为什么会出现密码提示叠在一起:管道两边的进程是同时启动的,它们都在监听标准输入要密码,所以提示信息会挤在一起,你输入的内容也可能被两个进程各读一部分,最后密码验证全失败。只要把它们的输入通道分开,或者让其中一个等另一个跑完,就能解决这个问题。
备注:内容来源于stack exchange,提问作者nesk

