最稳妥方案是用PHP自带openssl_encrypt批量加密文件:每次生成随机IV(16字节)并与密文拼接存储,密钥用hash('sha256', $password, true)衍生,大文件须流式分块处理,遍历目录用RecursiveDirectoryIterator严格校验isFile(),避免路径穿越与误操作。

用 openssl_encrypt 批量加密文件最稳妥
PHP 自带的 openssl_encrypt 是目前最可靠的选择,它支持 AES-256-CBC 等主流算法,且无需额外扩展(PHP 5.3+ 默认启用 OpenSSL)。别用 mcrypt —— 它在 PHP 7.2+ 已被彻底移除,强行启用会直接报 Call to undefined function mcrypt_encrypt()。
关键点在于:每次加密必须用新生成的随机 IV,并和密文一起保存,否则解密必然失败。IV 不用保密,但绝不能复用或硬编码。
- 使用
openssl_random_pseudo_bytes(16)生成 AES-128 的 IV(AES-256 需 16 字节 IV,不是 32) - 密钥建议用
hash('sha256', $password, true)衍生,避免明文密码直传 - 加密后把
$iv . $ciphertext拼接存为文件,或另存 JSON 元数据(如{"iv":"xxx","data":"yyy"})
遍历目录时注意 is_file() 和路径安全
批量操作最容易踩的坑是误加密子目录、符号链接,或因路径拼接出错导致文件写到错误位置。不要用 glob("*.txt") 这类简单方式 —— 它不递归,也过滤不了隐藏文件和非正规后缀。
推荐用 RecursiveDirectoryIterator 配合 is_file() 显式判断:
立即学习“PHP免费学习笔记(深入)”;
$iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iter as $file) {
if ($file->isFile() && !str_starts_with($file->getFilename(), '.')) {
$path = $file->getPathname();
// 加密逻辑放这里
}
}
- 务必检查
$file->isFile(),跳过目录、链接、socket 等非普通文件 - 用
realpath($dir)规范起始路径,防止相对路径穿越(如../../etc/passwd) - 加密目标文件建议另存为
$file->getPathname() . '.enc',原文件不动,避免加密中断导致数据丢失
加密大文件时必须分块读写,否则内存爆掉
直接 file_get_contents() 读一个 500MB 的日志文件,PHP 进程大概率被系统 kill。得用流式处理:打开源文件和目标文件句柄,每次读 8192 字节,加密后写入,循环到底。
注意 openssl_encrypt 不能直接流式加密 —— 它只处理完整数据块。所以得手动实现 CBC 模式分块:首块用 IV,后续块用上一块密文做 IV(即标准 CBC 链式)。
- 实际更简单:改用
openssl_encrypt($chunk, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv),但仅对首块传真实 IV;后续块传前一块密文末 16 字节作为新 IV - 或者偷懒一点:用
openssl命令行(需系统支持),通过popen()管道调用,避开 PHP 内存限制 - 无论如何,加密前先
fstat($fp_in)['size']获取大小,超过 10MB 就切流,别心存侥幸
解密失败?先检查 IV 是否被截断或编码损坏
90% 的解密失败不是密钥错,而是 IV 损坏。常见场景:把二进制 IV 直接 echo 到网页、存进 MySQL TEXT 字段没设 binary、用 base64_encode 存但解密时忘了 base64_decode。
如果加密后文件开头是乱码但长度正常,基本就是 IV 没对齐。AES-CBC 要求输入长度是 16 字节整数倍,不足要 PKCS#7 填充 —— openssl_encrypt 默认已处理,但你自己手写填充逻辑就容易出错。
- 调试时先用固定 IV(如
str_repeat("\x00", 16))和固定密钥跑通单文件,再换随机 IV - 保存 IV 时统一用
base64_encode($iv),解密时base64_decode($iv_b64),别混用 hex/utf8 - 如果加密后文件比原文小(比如少了 1~15 字节),说明填充逻辑漏了,或者用了
OPENSSL_ZERO_PADDING却没手动补零
真正麻烦的是密钥管理 —— 加密容易,怎么安全存密钥、怎么让不同机器解密同一批文件、怎么轮换密钥而不丢数据,这些没法靠几个函数解决。先跑通单机单密钥流程,再考虑加 KMS 或 HSM。



















