首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP Session 在客戶端禁用 Cookie 時的三種解決方案

     編輯:PHP 相關知識

PHP Session 預設透過 Cookie 傳遞 Session ID (預設名稱 PHPSESSID)。當使用者禁用 Cookie,Session 將失效。本文提供三種無 Cookie 環境下維持 Session 的方案,並分析優缺點與安全性。

方案一:開啟 session.use_trans_sid (自動 URL 重寫)

PHP 可自動在所有相對連結、表單 action 中加入 PHPSESSID=... 參數。

啟用方式

; php.ini
session.use_trans_sid = 1
; 或運行時
ini_set('session.use_trans_sid', '1');
session_start();

限制與風險

  • 僅重寫**相對 URL**,絕對 URL 不處理
  • Session ID 暴露於 URL,易被記錄在瀏覽器歷史、Proxy 日誌、Referer 標頭中 → **Session Fixation / Hijacking 風險極高**
  • 不適用於 AJAX、API 請求
  • 生產環境強烈不建議啟用

方案二:手動透過 URL/隱藏表單傳遞 Session ID

開發者自行在連結、表單中加入 Session ID。

連結傳遞

<?php
session_start();
$sid = session_id();
?>
<a href="page2.php?sid=<?= htmlspecialchars($sid) ?>">下一頁</a>

接收端還原

<?php
// page2.php
session_id($_GET['sid'] ?? ''); // 必須在 session_start() 之前
session_start();
// 現在 $_SESSION 可用

隱藏表單

<form method="post" action="process.php">
    <input type="hidden" name="sid" value="<?= htmlspecialchars(session_id()) ?>">
    <!-- 其他欄位 -->
</form>

優缺點

  • 完全可控,適用於 AJAX (Header 傳遞)
  • 同樣有 Session ID 暴露風險,需配合 HTTPS + 短期過期 + IP/UA 綁定
  • 維護成本高:每個連結/表單都要手動加入

方案三:伺服器端儲存 Session ID 對應關係 (推薦)

將 Session ID 儲存在資料庫/Redis/檔案,透過**唯一辨識碼** (如使用者 ID、API Token、簽名 URL) 關聯,而非直接暴露 Session ID。

資料庫 Schema 範例

CREATE TABLE user_sessions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    session_id VARCHAR(255) NOT NULL, -- PHP 原生 Session ID
    token VARCHAR(64) NOT NULL UNIQUE, -- 對外暴露的安全 Token
    user_agent VARCHAR(255),
    ip_address VARBINARY(16), -- INET6_ATON()
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP NOT NULL,
    INDEX idx_token (token),
    INDEX idx_user (user_id)
);

核心邏輯

<?php
class SessionManager {
    private PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    // 建立安全 Token 關聯
    public function createSession(int $userId, string $userAgent, string $ip): string {
        session_start();
        $phpSid = session_id();
        $token = bin2hex(random_bytes(32)); // 256-bit 安全 Token

        $stmt = $this->pdo->prepare("
            INSERT INTO user_sessions (user_id, session_id, token, user_agent, ip_address, expires_at)
            VALUES (?, ?, ?, ?, INET6_ATON(?), DATE_ADD(NOW(), INTERVAL 2 HOUR))
        ");
        $stmt->execute([$userId, $phpSid, $token, $userAgent, $ip]);

        return $token; // 回傳給前端,存於 localStorage / Header / URL
    }

    // 依 Token 還原 Session
    public function resumeByToken(string $token): bool {
        $stmt = $this->pdo->prepare("
            SELECT session_id FROM user_sessions
            WHERE token = ? AND expires_at > NOW()
        ");
        $stmt->execute([$token]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            return false;
        }

        session_id($row['session_id']);
        session_start();
        return true;
    }

    // 驗證並延長有效期
    public function validateAndExtend(string $token): bool {
        $stmt = $this->pdo->prepare("
            UPDATE user_sessions
            SET expires_at = DATE_ADD(NOW(), INTERVAL 2 HOUR)
            WHERE token = ? AND expires_at > NOW()
        ");
        $stmt->execute([$token]);
        return $stmt->rowCount() > 0;
    }
}

// 使用流程
// 1. 登入成功後
$token = $mgr->createSession($userId, $_SERVER['HTTP_USER_AGENT'], $_SERVER['REMOTE_ADDR']);
// 回傳 token 給前端 (JSON、設定 Cookie、或 URL 參數)

// 2. 後續請求 (API / AJAX / 無 Cookie 頁面)
// 前端在 Header 傳遞: Authorization: Bearer <token>
// 或 URL: ?token=xxx
$token = $_GET['token'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if ($mgr->resumeByToken($token)) {
    // $_SESSION 可用
    $mgr->validateAndExtend($token);
} else {
    // Token 無效/過期,要求重新登入
}

方案比較總表

方案實作難度安全性適用場景維護成本
use_trans_sid低⭐ (極低)舊系統快速相容低
手動 URL/表單中⭐⭐ (低)小型專案、特定流程高
Token 關聯 (推薦)中高⭐⭐⭐⭐⭐ (高)現代 Web App、API、SPA、無 Cookie 環境中 (封裝後低)

最佳實踐建議

  1. 預設使用 Cookie + HTTPS + Secure + HttpOnly + SameSite=Lax/Strict,覆蓋 99% 使用者。
  2. 針對無 Cookie 環境 (舊瀏覽器、隱私模式、特定嵌入場景),提供 **Token 方案** 作為備援。
  3. Token 必須:高熵隨機 (256-bit)、可撤銷、短效期、綁定 UA/IP、僅 HTTPS 傳輸。
  4. 定期清理過期 Token:DELETE FROM user_sessions WHERE expires_at < NOW()。
  5. 記錄 Session 活動日誌 (登入、恢復、失敗),便於稽核。

總結

禁用 Cookie 並非 Session 死胡同,但**直接暴露 Session ID 於 URL 是危險的反模式**。現代應用應採用 **不透明 Token + 伺服器端映射** 架構,兼顧相容性與安全性。將邏輯封裝為 SessionManager 服務,即可在既有專案平滑導入。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved