PHP 文件加密上传下载完整实现

```html PHP 文件加密上传与下载完整实现

PHP 文件加密上传与下载完整实现

在 Web 应用中,敏感文件(如合同、证件、日志)需防止未授权访问与中间窃取。本文提供基于 OpenSSL 的端到端加密方案:**上传时 AES-256-CBC 加密 → 存储密文 → 下载时解密返回明文**,全程不暴露密钥于客户端。

核心要点

  • 使用 openssl_encrypt()/openssl_decrypt() 实现对称加密
  • 密钥由服务端安全生成并硬编码(生产环境建议使用环境变量或密钥管理服务)
  • IV(初始向量)随机生成并随密文一同存储(非保密,但必须唯一)
  • 文件元信息(原名、大小、MIME)经 JSON 序列化后加密,保障完整性

1. 加密上传示例

<?php
$key = '32-byte-secret-key-for-aes-256!'; // 生产请使用 openssl_random_pseudo_bytes(32)
$uploadDir = __DIR__ . '/secure_uploads/';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];
    if ($file['error'] !== UPLOAD_ERR_OK) die('上传失败');

    $iv = openssl_random_pseudo_bytes(16);
    $plaintext = file_get_contents($file['tmp_name']);
    
    // 加密文件内容
    $ciphertext = openssl_encrypt($plaintext, 'AES-256-CBC', $key, 0, $iv);
    
    // 加密元数据(原文件名 + MIME)
    $meta = json_encode([
        'name' => $file['name'],
        'mime' => $file['type'],
        'size' => $file['size']
    ]);
    $metaEnc = openssl_encrypt($meta, 'AES-256-CBC', $key, 0, $iv);

    // 保存:IV + 密文 + 加密元数据(以 | 分隔)
    $filename = uniqid() . '.enc';
    file_put_contents($uploadDir . $filename, $iv . '|' . $ciphertext . '|' . $metaEnc);

    echo "✅ 上传成功,文件 ID:{$filename}";
}
?>

<form method="post" enctype="multipart/form-data">
    <input type="file" name="file" required>
    <button type="submit">加密上传</button>
</form>

2. 安全下载示例

<?php
$key = '32-byte-secret-key-for-aes-256!';
$uploadDir = __DIR__ . '/secure_uploads/';

if (isset($_GET['id']) && preg_match('/^[a-zA-Z0-9_-]+\.enc$/', $_GET['id'])) {
    $path = $uploadDir . $_GET['id'];
    if (!file_exists($path)) die('文件不存在');

    $data = file_get_contents($path);
    [$iv, $ciphertext, $metaEnc] = explode('|', $data, 3);

    // 解密元数据
    $metaRaw = openssl_decrypt($metaEnc, 'AES-256-CBC', $key, 0, $iv);
    $meta = json_decode($metaRaw, true);
    if (!$meta || !isset($meta['name'], $meta['mime'])) die('元数据损坏');

    // 解密文件内容
    $plaintext = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, 0, $iv);
    if ($plaintext === false) die('解密失败,请检查密钥');

    // 强制下载(避免浏览器渲染敏感内容)
    header('Content-Type: ' . $meta['mime']);
    header('Content-Disposition: attachment; filename="' . basename($meta['name']) . '"');
    header('Content-Length: ' . strlen($plaintext));
    echo $plaintext;
    exit;
}
?>

<a href="?id=abc123xyz.enc">⬇️ 下载加密文件</a>
⚠️ 安全提醒: • 密钥切勿写死在代码中(生产环境使用 $_ENV['ENCRYPTION_KEY'] 或 Vault); • 上传目录禁止 Web 直接访问(通过 .htaccess Deny from all 或 Nginx deny all;); • 建议增加用户权限校验、文件类型白名单、大小限制及防重放机制。

通过此方案,文件在传输与存储环节全程加密,即使服务器被入侵,攻击者也无法直接读取原始内容。安全不是功能,而是贯穿设计的基石。

```