本文目录导读:

在PHP项目中实现密码加密存储,千万不要使用 md5() 或 sha1() 这类简单哈希函数,因为它们已经不再安全。
目前最推荐、最安全的方法是使用 PHP 内置的 password_hash() 函数和 password_verify() 函数,PHP 默认使用 bcrypt 算法,这是目前最主流的密码哈希算法。
以下是详细的实现步骤和最佳实践。
核心函数:密码加密与验证
1 注册时:生成密码哈希
使用 password_hash() 对用户输入的密码进行加密。
<?php
// 用户注册时提交的密码
$plainPassword = $_POST['password'] ?? '';
// 使用 bcrypt 算法生成哈希(默认成本系数为 10)
$hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);
// 将 $hashedPassword 存储到数据库(varchar(255) 足够)
// INSERT INTO users (username, password) VALUES ('某个用户名', '$hashedPassword');
echo $hashedPassword; // $2y$10$SomeSaltAndHash...
?>
PASSWORD_BCRYPT:指定使用 bcrypt 算法。PASSWORD_DEFAULT:推荐选项,它会使用当前 PHP 版本推荐的最强算法(目前是 bcrypt,未来可能是 Argon2),使用这个选项,代码在未来 PHP 版本升级时能自动使用更强的算法。$hashedPassword:包含算法、盐(盐是自动随机生成的)和哈希值的字符串。非常安全。
2 登录时:验证密码
使用 password_verify() 比对用户输入的密码与数据库中存储的哈希值。
<?php
// 用户登录时输入密码
$inputPassword = $_POST['password'] ?? '';
// 从数据库读取该用户的哈希密码(假设从数据库查询得到)
$storedHash = '$2y$10$SomeSaltAndHash...'; // 数据库中的值
// 验证密码
if (password_verify($inputPassword, $storedHash)) {
// 密码正确,登录成功
echo "登录成功!";
// 启动 session,跳转页面等
} else {
// 密码错误
echo "密码错误!";
}
?>
password_verify()自动从存储的哈希值中提取出算法、成本因子和盐,然后对输入的密码进行同样的哈希计算,最后比较结果,整个过程安全且透明。
数据库设计
创建一个 users 表来存储用户信息,password 字段需要足够长来容纳哈希字符串。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, -- 存储哈希值,长度必须至少 60(bcrypt 的输出为 60 个字符),建议 255 以兼容未来算法
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
进阶:使用更现代的 Argon2 算法
PHP 7.2+ 支持 Argon2 算法,它比 bcrypt 更先进、更抗 GPU 和 ASIC 攻击。
<?php
// 使用 Argon2 算法(PHP 7.2+ 需要安装 libsodium 或使用 PHP 编译的 --with-password-argon2 选项)
$hashedPassword = password_hash($plainPassword, PASSWORD_ARGON2I);
// 或者使用 Argon2id (PHP 7.3+ 推荐,兼顾抗侧信道攻击和抗 GPU 攻击)
$hashedPassword = password_hash($plainPassword, PASSWORD_ARGON2ID);
// 验证方式完全一样
if (password_verify($inputPassword, $storedHash)) {
// 密码正确
}
- 如果你不确定服务器是否支持 Argon2,使用
PASSWORD_DEFAULT是安全的选择,PHP 版本默认算法切换为 Argon2 时,你的代码会自动适应。
最佳实践
-
永远不要在数据库中存储明文密码。
-
使用
password_hash()和password_verify(),不要自己拼接盐或使用md5、sha1、sha256。 -
为
password_hash()设置合适的cost因子(bcrypt 的成本系数,控制哈希计算速度)。- 默认是
10(约 0.1 秒)。 - 如果你希望更慢一点(增加破解成本),可以调高,
12。$options = ['cost' => 12]; $hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT, $options);
- 注意:成本太高会影响用户体验(登录变慢),建议根据你的服务器性能,将哈希计算时间控制在 1 - 0.5 秒之间。
- 默认是
-
如果需要升级哈希算法或成本因子(例如将成本从 10 升级到 12),可以在用户登录成功时,用新的算法重新对其密码进行哈希,然后更新数据库,这就是 密码哈希重哈希。
if (password_verify($inputPassword, $storedHash)) { // 如果当前哈希不是用最新算法或最优成本因子 if (password_needs_rehash($storedHash, PASSWORD_DEFAULT, ['cost' => 12])) { $newHash = password_hash($inputPassword, PASSWORD_DEFAULT, ['cost' => 12]); // 更新数据库:UPDATE users SET password = '$newHash' WHERE id = $userId; } // 登录成功 } -
使用 HTTPS,密码在传输过程中必须是加密的,否则即使服务器存储很安全,中间人攻击也能获取明文密码。
-
输入长度限制:在客户端(HTML)和服务器端对密码长度进行合理限制(例如最长 64 或 72 个字符,因为 bcrypt 内部处理 72 字节),这可以防止恶意用户提交超长密码导致拒绝服务攻击(DOS)。
总结示例代码
注册处理(register.php)
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$plainPassword = $_POST['password'] ?? '';
// 验证输入...
if (strlen($plainPassword) < 8) {
die("密码不能少于8个字符");
}
// 生成密码哈希
$hashedPassword = password_hash($plainPassword, PASSWORD_DEFAULT);
// 插入数据库(使用 PDO 预处理语句)
// $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
// $stmt->execute([$username, $hashedPassword]);
echo "注册成功!";
}
?>
登录处理(login.php)
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$inputPassword = $_POST['password'] ?? '';
// 从数据库查询用户(使用 PDO 预处理语句)
// $stmt = $pdo->prepare("SELECT id, password FROM users WHERE username = ?");
// $stmt->execute([$username]);
// $user = $stmt->fetch();
if ($user && password_verify($inputPassword, $user['password'])) {
// 可选:检查是否需要更新哈希
if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
$newHash = password_hash($inputPassword, PASSWORD_DEFAULT);
// 更新数据库...
}
$_SESSION['user_id'] = $user['id'];
header('Location: dashboard.php');
exit;
} else {
echo "用户名或密码错误";
}
}
?>
通过以上方法,你的 PHP 项目就能实现安全、现代、符合业界标准的密码加密存储。