您好!欢迎访问家园网-www.jy.wang!

家园网

PHP 使用 Session 和预处理语句实现安全登录验证

网络 作者:本站 点击:

PHP 使用 Session 和预处理语句实现安全登录验证

一、安全登录验证流程

  1. 数据库连接‌ - 使用PDO或MySQLi建立安全连接

  2. 用户输入处理‌ - 过滤和验证输入数据

  3. 预处理查询‌ - 使用参数化查询验证凭证

  4. 会话管理‌ - 设置安全会话变量

  5. 状态检查‌ - 验证会话有效性

  6. 登出处理‌ - 安全销毁会话

二、完整安全实现代码

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;
}
?>

安全措施:

  1. 输入过滤使用filter_input

  2. 参数化查询防止SQL注入

  3. password_verify安全验证密码哈希

  4. 会话ID再生防止会话固定

  5. 记录用户IP和UA防止会话劫持

  6. 错误日志记录而不暴露系统信息

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;
?>

三、安全最佳实践

  1. 数据库层面

    • 使用预处理语句(PDO或MySQLi)16

    • 设置数据库用户最小权限3

    • 定期备份数据库

  2. 密码存储

    • 使用password_hash()password_verify()3

    • 成本因子至少为12

  3. 会话安全

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => $_SERVER['HTTP_HOST'],
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
    • 启用HTTPS传输3

    • 设置会话cookie安全标志3

  1. 输入验证

    • 白名单验证优于黑名单3

    • 类型和长度验证

  2. 错误处理

    • 生产环境关闭错误显示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防止XSS

  • CSRF保护(示例中未展示,实际应添加)

  • 响应式设计

通过以上实现,我们建立了一个完整的、安全的PHP登录系统,有效防止了SQL注入和其他常见Web攻击。

标签: