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

PHP Cookie 完整指南:設定、讀取、刪除、安全屬性與實戰登入紀錄範例

     編輯:PHP 相關知識

前言

Cookie 是伺服器要求瀏覽器在本地儲存的小型鍵值對,隨後續請求自動攜帶。PHP 提供 setcookie()/setrawcookie() 介面操作 Cookie。本文涵蓋完整參數、安全屬性、刪除技巧、子域共享、實戰「記住我」登入流程與現代替代方案。

setcookie() 完整簽名

setcookie(
    string $name,
    string $value = "",
    int $expires_or_options = 0,
    string $path = "",
    string $domain = "",
    bool $secure = false,
    bool $httponly = false
): bool

// PHP 7.3+ 支援陣列選項
setcookie($name, $value, [
    'expires'  => time() + 3600,
    'path'     => '/',
    'domain'   => '.example.com',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax', // PHP 7.3+
]);

參數詳細說明

參數說明建議值
nameCookie 名稱(不可含 ; , = 空白)英文底線
value值(自動 urlencode,setrawcookie 不編碼)結構化資料請 json_encode
expiresUnix 時間戳,0 或省略 = 瀏覽器關閉失效(Session Cookie)記住我:time()+30*86400
path生效路徑,/ 為全站/
domain生效域名,.example.com 含子域.example.com
secure僅 HTTPS 傳輸HTTPS 站必開
httponlyJS 無法讀取 (document.cookie)1 (防 XSS)
samesite跨站請求攜帶策略:Strict/Lax/NoneLax (平衡安全與體驗)

讀取與刪除

 time() - 3600, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true]);

安全屬性三大支柱

  1. HttpOnly:防止 XSS 竊取 Cookie(document.cookie 讀不到)。
  2. Secure:防止中間人攻擊竊取/竄改,僅 HTTPS 傳輸。
  3. SameSite:
    • Strict:完全禁止跨站攜帶(連結跳轉也不帶)。
    • Lax:允許安全跨站(GET 連結、預載),預設推薦。
    • None:需 Secure,允許所有跨站(第三方 iframe、嵌入服務)。

實戰:完整「記住我」登入流程

資料表

CREATE TABLE users (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    remember_token VARCHAR(64) DEFAULT NULL,
    token_expires INT UNSIGNED DEFAULT NULL
);

登入處理

prepare('SELECT id, password_hash FROM users WHERE username = ?');
    $stmt->execute([$username]);
    $user = $stmt->fetch();

    if ($user && password_verify($password, $user['password_hash'])) {
        // 正常 Session 登入
        session_regenerate_id(true);
        $_SESSION['user_id'] = $user['id'];

        // 記住我:產生隨機 Token 存資料庫 + 寫 Cookie
        if ($remember) {
            $token = bin2hex(random_bytes(32));
            $expires = time() + 30 * 86400; // 30 天
            $pdo->prepare('UPDATE users SET remember_token=?, token_expires=? WHERE id=?')
                ->execute([$token, $expires, $user['id']]);
            setcookie('remember_token', $token, [
                'expires'  => $expires,
                'path'     => '/',
                'domain'   => '.example.com',
                'secure'   => true,
                'httponly' => true,
                'samesite' => 'Lax',
            ]);
        }
        header('Location: /dashboard');
        exit;
    }
    $error = '帳號或密碼錯誤';
}
// ... 顯示登入表單

自動登入中介邏輯

prepare('SELECT id FROM users WHERE remember_token = ? AND token_expires > ?');
    $stmt->execute([$token, time()]);
    if ($user = $stmt->fetch()) {
        session_regenerate_id(true);
        $_SESSION['user_id'] = $user['id'];
        // 可選:滑動延長 Token 有效期
        $newExp = time() + 30 * 86400;
        $pdo->prepare('UPDATE users SET token_expires = ? WHERE id = ?')->execute([$newExp, $user['id']]);
        setcookie('remember_token', $token, ['expires' => $newExp, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
    } else {
        // Token 無效/過期,清除 Cookie
        setcookie('remember_token', '', ['expires' => time() - 3600, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true]);
    }
}

登出

prepare('UPDATE users SET remember_token=NULL, token_expires=NULL WHERE id=?')->execute([$userId]);
}
$_SESSION = [];
session_destroy();
setcookie('remember_token', '', ['expires' => time() - 3600, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true]);
header('Location: /login');

子域共享 Cookie

 time() + 3600,
    'path'     => '/',
    'domain'   => '.example.com', // 關鍵:前綴點號
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

注意:session.cookie_domain 同理設定 .example.com 即可跨子域共享 Session。

常見坑點與排查

現象原因解決
setcookie() 回傳 true 但瀏覽器無 Cookie輸出已開始(空白、BOM、echo)、headers_sent() 為 true確保 setcookie() 在任何輸出前、檔案無 BOM、ob_start()
Cookie 值被截斷/異常值含 ; , = 空白、未 urlencodejson_encode() 結構化資料、setrawcookie() 自行編碼
跨子域不共享domain 未設 .example.com、SameSite=Strict 阻擋設 domain、改 Lax
刪除無效path/domain/secure/httponly 不完全一致刪除時參數與設定時完全一致
開發環境 HTTPS 無 CookieSecure=1 但本地無 HTTPS本地用 lvh.me + mkcert、或開發環境關 Secure

現代替代:JWT + HttpOnly Cookie

將 JWT 簽名後的 Token 存入 HttpOnly; Secure; SameSite=Lax Cookie,而非 LocalStorage,兼具無狀態擴充性與 XSS 防護。伺服器端僅驗證簽名,無需 Session 儲存。

結語

Cookie 是瀏覽器端狀態的基石。掌握 完整參數、安全三屬性、刪除對稱性、子域共享、記住我 Token 輪換,並建立「敏感 Cookie 必 HttpOnly+Secure+SameSite」的鐵律,即能在便利與安全間取得平衡。架構演進期可平滑遷移至 JWT Cookie 模式。

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