加密前必须先复制临时副本再操作,因直接加密被其他进程写入的文件会导致数据截断;需用os.Stat检查文件类型,避免对设备文件等非普通文件加密。

加密前必须确认文件是否已被打开或锁定
Go 程序直接对正在被其他进程写入的文件(如日志、数据库 WAL 文件)调用 os.OpenFile 并加密,极易导致数据截断或损坏。这不是 Go 特有,而是操作系统层面的竞态问题。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
os.Stat检查ModePerm外的属性,比如os.ModeCharDevice或os.ModeSocket,快速排除非普通文件 - 尝试以
os.O_RDONLY | os.O_EXCL打开(注意:Windows 不支持O_EXCL对普通文件生效,需改用syscall.LockFileEx) - 更稳妥的做法是先复制一份临时副本(
io.Copy+os.CreateTemp),再对副本加密 —— 即使源文件被写入,副本内容已固定
选对 cipher.Block 而不是硬套 AES-256-GCM
AES-256-GCM 看起来安全又现代,但实际落地时容易踩坑:它要求每个密钥只能用于一个 nonce,且 nonce 绝对不可复用;而文件级加密常需“单密钥加密多文件”,若直接用时间戳或递增整数当 nonce,会破坏安全性。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 优先用
golang.org/x/crypto/nacl/secretbox:自带随机 nonce 生成、无需手动管理,适合文件一次性加密场景 - 若必须用 AES-GCM,nonce 必须随密文一起持久化(通常前置 12 字节),且每次调用
cipher.NewGCM前用crypto/rand.Read生成新 nonce - 避免使用 ECB 模式 ——
crypto/aes.NewCipher返回的 block 本身不带模式,ECB 需自行实现,且完全不推荐
密钥来源不能写死在代码里
把密钥硬编码进 Go 二进制(哪怕 base64 编码)等于没加密。Go 编译后的可执行文件能被 strings 命令轻易提取出明文密钥。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 生产环境必须从外部注入:
os.Getenv("ENCRYPTION_KEY")或读取/run/secrets/下的文件(Docker Swarm / Kubernetes) - 开发阶段可用
golang.org/x/crypto/pbkdf2+ 用户口令派生密钥,但务必加盐(盐值存于文件头,不保密) - 切勿用
md5或sha256直接哈希口令 —— 这些函数太快,易被暴力破解
加密后文件元信息要显式保留
加密操作默认只处理文件内容,原始的 ModTime、Chmod 权限、甚至 SELinux 上下文都会丢失。用户解密后发现文件时间变成“现在”,权限变成 0644,往往误以为流程出错。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 加密前用
os.Stat获取os.FileInfo,保存ModTime()、Mode() - 加密完成后,用
os.Chtimes和os.Chmod主动还原 - 若需兼容 Linux 扩展属性(如 SELinux),得调用
syscall.Setxattr(需 cgo),普通场景可忽略
真正麻烦的不是加密逻辑本身,而是如何让加密后的文件在系统里“看起来和原来一样”——时间、权限、甚至 inode 号(后者无法保留,只能接受)。这些细节不处理,运维同学第一眼就会质疑方案可靠性。


















