核心概念
HttpOnly 與客戶端儲存
標記為 HttpOnly 的 Cookie 無法透過客戶端 JavaScript(cookieStore 或 document.cookie)存取或修改。然而,瀏覽器會自動將有效的 HttpOnly Cookie 附加到外送的 HTTP 請求標頭(Cookie)。
- 檢查目前的 HttpOnly 值:透過
get_network_request查看任何外送 HTTP 請求的Cookie請求標頭。 - 檢查 Cookie 的建立或配置方式:查看登入/驗證回應的
Set-Cookie回應標頭。 - 檢查非 HttpOnly Cookie:使用
evaluate_script搭配現代的cookieStoreAPI(async () => await cookieStore.getAll())。
Session 策略:即時分頁 vs 隔離上下文
選擇正確的 session 環境以避免狀態污染(例如殘留的分析或驗證 token):
| 策略 | 使用時機 | 設定 / 拆卸 |
|---|---|---|
| 即時分頁(活躍頁面) | 診斷活躍使用者 session、即時 401/403 錯誤或目前狀態。 | 直接對目前選取的頁面進行操作。 |
全新狀態(isolatedContext) | 測試 Cookie 同意橫幅、首次造訪或零 Cookie 保證。 | 使用唯一的 isolatedContext(例如 "consent-audit-1")呼叫 new_page。完成後呼叫 close_page。 |
客戶端功能與限制
| 動作 | 客戶端 JavaScript(cookieStore / document.cookie) | DevTools 網路與上下文工具 |
|---|---|---|
| 讀取非 HttpOnly | ✅ async () => await cookieStore.getAll() | ✅ get_network_request(請求 Cookie) |
| 讀取 HttpOnly | ❌ 瀏覽器安全性封鎖 | ✅ get_network_request(請求 Cookie) |
檢查屬性(Domain、Path、SameSite、Expires) | ✅ async () => await cookieStore.getAll() | ✅ get_network_request(回應 Set-Cookie) |
| 修改 / 刪除非 HttpOnly | ✅ async () => await cookieStore.set(...) | N/A |
| 修改 / 刪除 HttpOnly | ❌ JavaScript 中靜默失敗 | ✅ 使用 new_page(isolatedContext: ...) 取得乾淨狀態 |
[!WARNING] 嘗試透過 JavaScript(
cookieStore.delete或document.cookie = "...; max-age=0")清除HttpOnlyCookie 將會靜默失敗。若要在未驗證或全新狀態下進行測試,請務必使用new_page搭配isolatedContext建立新的隔離上下文。
工作流程模式
1. 診斷驗證失敗與重新導向(401 / 403)
當認證頁面請求失敗、回傳 401/403 或重新導向至登入頁面時:
- 列出最近請求:使用
includePreservedRequests: true呼叫list_network_requests。 - 找出目標請求:定位失敗的請求(401/403)或重新導向(302/307)。
- 檢查外送的
Cookie標頭:使用reqid呼叫get_network_request。- 驗證是否附帶了
Cookie標頭,以及必要的 token(例如SESSION_ID、auth_token)是否已傳送。
- 驗證是否附帶了
- 觸發主動檢查(若無最近請求):
- 如果 Cookie 是在先前 session 中設定,且未列出任何網路呼叫,請觸發請求:
- 使用
reload: true的navigate_page,或 - 呼叫
evaluate_script搭配() => fetch(window.location.href)
- 使用
- 然後在新的請求上呼叫
get_network_request以檢查現行Cookie標頭。
- 如果 Cookie 是在先前 session 中設定,且未列出任何網路呼叫,請觸發請求:
- 追蹤設定請求:如果 Cookie 遺失或被拒絕:
- 檢查較早的登入/握手回應中的
Set-Cookie指示:- Path 不匹配:例如
Path=/api,但請求是針對/。 - Domain 不匹配:例如
Domain=api.example.com導致 Cookie 無法用於sub.example.com。 - HTTP 上的 Secure 標誌:
SecureCookie 絕不會透過未加密的http://傳送。 - SameSite 封鎖:
SameSite=StrictCookie 在跨站導航時會被省略。 - 過期:檢查
Expires或Max-Age是否已過期。
- Path 不匹配:例如
- 檢查較早的登入/握手回應中的
2. Cookie 橫幅與同意合規性測試
驗證在同意前或拒絕時,不會設定任何非必要或追蹤型 Cookie:
- 從乾淨狀態開始:使用專用名稱開啟一個全新的隔離上下文:
{"url": "<PAGE_URL>", "isolatedContext": "consent-test-1"} - 記錄基準 Cookie:在與橫幅互動之前,執行
evaluate_script搭配async () => await cookieStore.getAll()。 - 檢查過早的網路請求與問題:
- 呼叫
list_network_requests以確保在同意前無第三方追蹤信標觸發。 - 使用
types: ["issue"]呼叫list_console_messages以檢查是否有追蹤警告。
- 呼叫
- 與同意橫幅互動:
- 使用
take_snapshot擷取快照以找出「拒絕」或「全部拒絕」按鈕的uid。 - 使用
click點擊該按鈕。
- 使用
- 驗證 Cookie 差異:
- 在點擊後執行
evaluate_script搭配async () => await cookieStore.getAll(),以確認僅存在絕對必要或同意狀態的 Cookie。
- 在點擊後執行
- 測試同意撤銷(生命週期審計):
- 審計同意的撤回或偏好變更時:
- 找到並點擊「Cookie 設定」、「管理偏好設定」或頁尾的隱私觸發器(
take_snapshot→click)。 - 取消選取非必要類別,或點擊「全部撤銷」/「儲存偏好設定」。
- 重新查詢
cookieStore.getAll()以驗證先前接受的非必要 Cookie 已被清除或過期。 - 在後續動作上呼叫
list_network_requests,以確保不再觸發追蹤信標。
- 找到並點擊「Cookie 設定」、「管理偏好設定」或頁尾的隱私觸發器(
- 審計同意的撤回或偏好變更時:
- 拆除上下文:審計完成後呼叫
close_page,以防止殘留 Cookie 影響後續任務。
3. 審計 Cookie 安全性、SameSite 與 CHIPS(分區 Cookie)
- 快速路徑:原生 DevTools Issue(建議):
- 使用以下參數呼叫
list_console_messages:{ "types": ["issue"], "includePreservedMessages": true } - 檢查
CookieIssue條目,例如:SameSiteNoneInsecure:SameSite=None但未使用Secure。ThirdPartyCookiePhaseout:第三方 Cookie 被封鎖或限制。SchemefulSameSite:跨協議 Cookie 問題。PartitionedCookies:無效的 CHIPS 分區屬性。
- 使用以下參數呼叫
- 深度審計:Lighthouse 第三方 Cookie:
- 使用
mode: "navigation"和outputDirPath: "/tmp/lh-report"執行lighthouse_audit。 - 提取特定的 Cookie 審計結果,無需將完整報告載入上下文:
node -e "const r=require('/tmp/lh-report/report.json'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, displayValue: a?.displayValue, details: a?.details}))" - 此命令僅提取
third-party-cookies審計的分數與詳細資訊。
- 使用
- 手動驗證 Set-Cookie 標頭(針對特定 Cookie):
- 呼叫
get_network_request並傳入設定該 Cookie 的回應之reqid,以檢查Set-Cookie標頭的正確屬性,例如Secure、HttpOnly、SameSite、Domain與Partitioned。
- 呼叫