PHP 认证信息加密存储

wen PHP项目 2

本文目录导读:

PHP 认证信息加密存储

  1. 用户密码(必须用哈希,不能用加密)
  2. API 密钥/Token(必须用加密)
  3. 会话(Session)与 Cookie 安全
  4. 数据库中的其他敏感字段(如身份证号、手机号)
  5. 常见致命错误(务必避免)
  6. 选择策略

在 PHP 中加密存储认证信息(如密码、API密钥、令牌等)是安全实践的核心。最核心的原则是:密码用哈希(不可逆),密钥用加密(可逆)。

以下是针对不同类型的认证信息的加密存储方案详解:


用户密码(必须用哈希,不能用加密)

原则:密码绝对不能加密存储(因为加密可逆,一旦密钥泄露,所有密码暴露),必须使用单向哈希函数,并且要加盐。

推荐方案(现在最安全):

使用 PHP 原生的 password_hash()password_verify(),它们自动处理盐值,并使用 bcrypt 或 Argon2 算法。

<?php
// 注册时:存储哈希
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
// 存入数据库时,存 $hashedPassword
// 登录时:验证
$user = // 从数据库取出该用户的哈希值
if (password_verify($_POST['password'], $user['password_hash'])) {
    echo "登录成功";
} else {
    echo "密码错误";
}
// 如果需要重新计算哈希(例如升级算法)
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
    $newHash = password_hash($_POST['password'], PASSWORD_DEFAULT);
    // 更新数据库中的哈希
}
?>

兜底方案(老系统迁移):

如果你用 crypt()md5()请立即迁移,迁移方案是:登录时先验证旧哈希,如果通过则替换为新哈希。


API 密钥/Token(必须用加密)

场景:第三方应用需要读取用户数据,或系统需要存储外部服务的密钥(如 Stripe API Key),此时信息需要解密后使用,因此必须使用对称加密。

推荐方案(AES-256-GCM):

使用 PHP 7.2+ 的 sodium 扩展,或者 openssl_encrypt优先使用 OpenSSL 的 AES-256-GCM,因为它自带认证标签(防止篡改)。

<?php
// 定义密钥(存放到环境变量或配置文件,切勿硬编码在代码中)
// 生成方法:bin2hex(random_bytes(32))  => 64位十六进制字符串
$encryptionKey = getenv('APP_SECRET_KEY'); // 使用 .env 管理
/**
 * 加密函数
 */
function encryptData(string $plainText, string $key): string {
    // 生成初始化向量(IV)
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-gcm'));
    // 加密
    $cipherText = openssl_encrypt(
        $plainText,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag, // 认证标签(输出参数)
        '',
        16 // 标签长度(推荐16字节=128位)
    );
    // 将 IV 和 Tag 附加到密文上,方便解密
    return base64_encode($iv . $tag . $cipherText);
}
/**
 * 解密函数
 */
function decryptData(string $encryptedData, string $key): ?string {
    $decoded = base64_decode($encryptedData);
    // 提取 IV(前16字节)、Tag(中间16字节)、密文(剩余部分)
    $ivLength = openssl_cipher_iv_length('aes-256-gcm');
    $iv = substr($decoded, 0, $ivLength);
    $tag = substr($decoded, $ivLength, 16);
    $cipherText = substr($decoded, $ivLength + 16);
    $plainText = openssl_decrypt(
        $cipherText,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag
    );
    return $plainText === false ? null : $plainText;
}
// 使用示例
$apiKey = "sk_live_123456789";
$encrypted = encryptData($apiKey, $encryptionKey);
// 存储 $encrypted 到数据库
// 使用
$decrypted = decryptData($encrypted, $encryptionKey);
echo $decrypted; // 输出原始API Key
?>

会话(Session)与 Cookie 安全

这是认证信息的另一个载体,核心原则是 “服务端存储,客户端只给ID”

最佳实践:

  1. Session 文件:不要将敏感信息直接放在 $_SESSION 中明文保存,如果服务器被读取了,也照样泄露,建议只存用户 id,其他信息每次从数据库查。
  2. Session ID:使用 PHP 默认的安全设置:
    ini_set('session.use_strict_mode', '1');   // 严格模式
    ini_set('session.use_only_cookies', '1'); // 禁止URL传递Session ID
    session_set_cookie_params([
        'httponly' => true,   // 禁止JavaScript读取
        'secure' => true,     // 仅HTTPS传输
        'samesite' => 'Lax'   // 防止CSRF
    ]);
    session_start();

数据库中的其他敏感字段(如身份证号、手机号)

如果必须存储可逆的敏感个人信息(例如客服需要查看),单独拆一张表,并使用 PHP openssl_encrypt 加密,但解密逻辑严格限制权限


常见致命错误(务必避免)

  1. 使用 md5()/sha1() 存储密码:这些是快速哈希,极其容易被彩虹表或暴力破解。
  2. 硬编码密钥:密钥必须放在 .env 或环境变量中,绝不能提交到 Git 仓库,即使代码泄露,密钥也不泄露。
  3. 加密与哈希混淆:记住上面的原则,密码哈希,敏感数据加密。
  4. 没有使用 HTTPS:如果传输层是 HTTP,无论存储多安全,中间人攻击都能直接截获明文。

选择策略

数据类型 处理方式 算法/函数 备注
用户密码 单向哈希 password_hash() 绝对不要加密
OAuth Token 对称加密 openssl_encrypt() (AES-256-GCM) 需要时可解密
第三方 API Key 对称加密 openssl_encrypt() (AES-256-GCM) 需要时可解密
Session ID 由框架生成 session_start() 服务端存储,客户端只存ID
敏感个人信息 对称加密 openssl_encrypt() 谨慎解密,操作留痕

补充一点: 如果使用 Laravel、Symfony 等框架,这些机制已经封装好,Laravel 的 Hash facade 和 Crypt facade 分别对应 “密码哈希” 和 “加密/解密”,直接使用即可,底层已经处理好了这些安全细节。

抱歉,评论功能暂时关闭!