PHP 使用 Session 和预处理语句实现安全登录验证
一、安全登录验证流程
数据库连接 - 使用PDO或MySQLi建立安全连接
用户输入处理 - 过滤和验证输入数据
预处理查询 - 使用参数化查询验证凭证
会话管理 - 设置安全会话变量
状态检查 - 验证会话有效性
登出处理 - 安全销毁会话
二、完整安全实现代码
1. 数据库连接 (db_connect.php)
<?php
// 使用PDO连接数据库,设置错误模式和字符集
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=user_db;charset=utf8mb4',
'db_user',
'secure_password_123',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false, // 禁用模拟预处理
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
die("系统维护中,请稍后再试");
}
?>安全特性:
使用UTF-8MB4字符集支持完整Unicode
禁用模拟预处理确保真正的参数化查询
异常模式捕获数据库错误
错误日志记录而不直接显示给用户
2. 登录处理 (login.php)
<?php
require 'db_connect.php';
session_start();
// 1. 过滤和验证输入
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$password = $_POST['password'] ?? ''; // 密码不进行HTML过滤
if (empty($username) || empty($password)) {
$_SESSION['error'] = '用户名和密码不能为空';
header('Location: login_form.php');
exit;
}
// 2. 预处理查询验证用户
try {
$stmt = $pdo->prepare("
SELECT id, username, password_hash, last_login
FROM users
WHERE username = :username
AND account_locked = 0
");
$stmt->bindValue(':username', $username, PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch();
// 3. 验证密码
if ($user && password_verify($password, $user['password_hash'])) {
// 4. 设置安全会话
session_regenerate_id(true); // 防止会话固定攻击
$_SESSION['user'] = [
'id' => $user['id'],
'name' => $user['username'],
'ip' => $_SERVER['REMOTE_ADDR'],
'agent' => $_SERVER['HTTP_USER_AGENT'],
'last_activity' => time()
];
// 更新最后登录时间
$updateStmt = $pdo->prepare("
UPDATE users
SET last_login = NOW()
WHERE id = :id
");
$updateStmt->bindValue(':id', $user['id'], PDO::PARAM_INT);
$updateStmt->execute();
header('Location: dashboard.php');
exit;
} else {
$_SESSION['error'] = '用户名或密码错误';
header('Location: login_form.php');
exit;
}
} catch (PDOException $e) {
error_log("Login error: " . $e->getMessage());
$_SESSION['error'] = '登录过程中发生错误';
header('Location: login_form.php');
exit;
}
?>安全措施:
输入过滤使用
filter_input参数化查询防止SQL注入
password_verify安全验证密码哈希会话ID再生防止会话固定
记录用户IP和UA防止会话劫持
错误日志记录而不暴露系统信息
3. 登录状态检查 (auth_check.php)
<?php
session_start();
// 1. 检查基本会话
if (empty($_SESSION['user'])) {
header('Location: login.php?redirect=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}
// 2. 安全验证
$user = $_SESSION['user'];
$current_ip = $_SERVER['REMOTE_ADDR'];
$current_agent = $_SERVER['HTTP_USER_AGENT'];
// 检查IP和用户代理是否变化
if ($user['ip'] !== $current_ip || $user['agent'] !== $current_agent) {
session_unset();
session_destroy();
header('Location: login.php?reason=session_hijack');
exit;
}
// 3. 检查会话超时(30分钟)
if (time() - $user['last_activity'] > 1800) {
session_unset();
session_destroy();
header('Location: login.php?reason=timeout');
exit;
}
// 4. 更新最后活动时间
$_SESSION['user']['last_activity'] = time();
?>安全验证:
IP和用户代理验证
会话超时机制
活动时间更新
4. 登出处理 (logout.php)
<?php
session_start();
// 1. 清空会话数据
$_SESSION = [];
// 2. 删除会话cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
// 3. 销毁会话
session_destroy();
// 4. 重定向
header("Location: login.php");
exit;
?>三、安全最佳实践
数据库层面
使用预处理语句(PDO或MySQLi)16
设置数据库用户最小权限3
定期备份数据库
密码存储
使用
password_hash()和password_verify()3成本因子至少为12
会话安全
session_set_cookie_params([ 'lifetime' => 0, 'path' => '/', 'domain' => $_SERVER['HTTP_HOST'], 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]);
启用HTTPS传输3
设置会话cookie安全标志3
输入验证
白名单验证优于黑名单3
类型和长度验证
错误处理
生产环境关闭错误显示3
记录到安全日志文件
四、完整登录表单示例 (login_form.php)
<?php
session_start();
$error = $_SESSION['error'] ?? '';
unset($_SESSION['error']);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>系统登录</title>
<style>
body { font-family: Arial, sans-serif; background: #f5f5f5; }
.login-box { max-width: 400px; margin: 100px auto; padding: 20px; background: white; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
.error { color: red; margin-bottom: 15px; }
input { width: 100%; padding: 10px; margin: 8px 0; box-sizing: border-box; }
button { background: #4CAF50; color: white; padding: 10px; border: none; cursor: pointer; width: 100%; }
</style>
</head>
<body>
<div>
<h2>系统登录</h2>
<?php if ($error): ?>
<div><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="post" action="login.php">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
<button type="submit">登录</button>
</form>
</div>
</body>
</html>安全特性:
输出使用
htmlspecialchars防止XSSCSRF保护(示例中未展示,实际应添加)
响应式设计
通过以上实现,我们建立了一个完整的、安全的PHP登录系统,有效防止了SQL注入和其他常见Web攻击。