PHP会话固定攻击怎么防

wen PHP项目 1

PHP会话固定攻击(Session Fixation Attack)是一种常见的Web安全威胁,攻击者通过让受害者使用攻击者已知的Session ID,从而在受害者登录后获取其会话控制权。

PHP会话固定攻击怎么防

防护方案

核心防护:登录后强制更换Session ID

<?php
// 用户登录成功后立即执行
session_regenerate_id(true);  // 生成新的Session ID并删除旧ID

完整的安全登录示例

<?php
session_start();
// ... 验证用户凭证 ...
if ($login_success) {
    // 清除所有session数据
    $_SESSION = array();
    // 删除会话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"]
        );
    }
    // 彻底销毁旧会话
    session_destroy();
    // 开始新会话
    session_start();
    // 生成新会话ID
    session_regenerate_id(true);
    // 设置用户会话
    $_SESSION['user_id'] = $user_id;
    $_SESSION['login_time'] = time();
    $_SESSION['fingerprint'] = generateFingerprint(); // 见下文
}

增强安全措施

设置安全的Session配置

<?php
// php.ini 或代码中的配置
ini_set('session.use_strict_mode', '1');           // 拒绝未初始化的Session ID
ini_set('session.use_only_cookies', '1');          // 仅使用Cookie传递Session ID
ini_set('session.cookie_httponly', '1');           // 禁止JavaScript访问Session Cookie
ini_set('session.cookie_secure', '1');             // HTTPS下使用
ini_set('session.cookie_samesite', 'Lax');         // 防CSRF
ini_set('session.gc_maxlifetime', '1800');         // 会话超时时间(秒)

用户指纹验证

<?php
function generateFingerprint() {
    // 使用用户特征生成指纹
    return hash('sha256', 
        $_SERVER['HTTP_USER_AGENT'] . 
        getClientIP() . 
        (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '')
    );
}
function validateFingerprint() {
    if (isset($_SESSION['fingerprint'])) {
        return $_SESSION['fingerprint'] === generateFingerprint();
    }
    return false;
}
// 每次请求验证
if (!validateFingerprint()) {
    session_regenerate_id(true);  // 指纹不匹配时重新生成
}

完整的防护类

<?php
class SessionSecurity {
    public static function secureSessionStart() {
        // 严格模式
        ini_set('session.use_strict_mode', '1');
        ini_set('session.use_only_cookies', '1');
        ini_set('session.cookie_httponly', '1');
        ini_set('session.cookie_secure', '1');
        ini_set('session.cookie_samesite', 'Lax');
        session_start();
        // 验证Session ID是否由服务器生成
        if (!isset($_SESSION['initiated'])) {
            session_regenerate_id(true);
            $_SESSION['initiated'] = true;
            $_SESSION['ip'] = getClientIP();
            $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
        }
        // 验证IP和User-Agent
        if ($_SESSION['ip'] !== getClientIP() || 
            $_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
            self::destroySession();
            session_start();
            session_regenerate_id(true);
        }
    }
    public static function loginSession($userId) {
        // 登录成功后重置
        self::destroySession();
        session_start();
        session_regenerate_id(true);
        $_SESSION['user_id'] = $userId;
        $_SESSION['ip'] = getClientIP();
        $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
        $_SESSION['last_activity'] = time();
        $_SESSION['initiated'] = true;
    }
    public static function destroySession() {
        $_SESSION = array();
        if (ini_get("session.use_cookies")) {
            $params = session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params["path"], $params["domain"],
                $params["secure"], $params["httponly"]
            );
        }
        session_destroy();
    }
    private static function getClientIP() {
        // 获取真实IP(考虑代理)
        $ip = $_SERVER['REMOTE_ADDR'];
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        return $ip;
    }
}
// 使用示例
SessionSecurity::secureSessionStart();

其他重要预防措施

<?php
// 定期更换Session ID(防止固定攻击的持久化)
if (isset($_SESSION['last_activity']) && 
    (time() - $_SESSION['last_activity'] > 600)) {
    session_regenerate_id(true);
    $_SESSION['last_activity'] = time();
}
// 设置合理的过期时间
$_SESSION['expire_time'] = time() + 3600; // 1小时
if (time() > $_SESSION['expire_time']) {
    SessionSecurity::destroySession();
    header('Location: /login.php');
    exit();
}

安全建议清单

  • 强制更换Session ID:登录成功、权限变更时务必调用session_regenerate_id(true)
  • 启用Strict Modesession.use_strict_mode = 1,拒绝未初始化的Session ID
  • 使用HTTPS:确保session.cookie_secure = 1
  • 设置Session过期时间:避免会话无限期有效
  • 使用随机和长的Session ID:PHP默认使用哈希算法,保持默认即可
  • 验证User-Agent和IP:但注意IP变化需谨慎处理
  • SameSite属性:设置为LaxStrict
  • 不要接受URL传递的Session ID
  • 不要长期使用同一个Session ID

更高级的防护:双重验证

<?php
// 生成随机会话令牌
$session_token = bin2hex(random_bytes(32));
$_SESSION['session_token'] = $session_token;
// 在前端页面保存(通过cookie或header)
setcookie('session_token', $session_token, [
    'expires' => time() + 3600,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
// 每次请求验证
if (isset($_COOKIE['session_token']) && 
    isset($_SESSION['session_token']) && 
    $_COOKIE['session_token'] !== $_SESSION['session_token']) {
    // 令牌不匹配,可能是攻击
    SessionSecurity::destroySession();
    header('Location: /login.php');
    exit();
}

关键点在于:登录成功后必须更换Session ID,这是最核心、最有效的防护措施,其他措施都是辅助性的,可以显著提高安全性,建议综合使用上述所有方法,构建多层次的防护体系。

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