핵심 개념
HttpOnly vs 클라이언트 측 저장소
HttpOnly로 표시된 쿠키는 클라이언트 측 JavaScript(cookieStore 또는 document.cookie)에서 접근하거나 수정할 수 없습니다. 그러나 브라우저는 활성화된 HttpOnly 쿠키를 나가는 HTTP 요청 헤더(Cookie)에 자동으로 첨부합니다.
- 현재
HttpOnly값을 검사하려면:get_network_request를 통해 나가는 HTTP 요청의Cookie요청 헤더를 확인합니다. - 쿠키가 어떻게 생성되었거나 구성되었는지 검사하려면: 로그인/인증 응답의
Set-Cookie응답 헤더를 확인합니다. HttpOnly가 아닌 쿠키를 검사하려면:evaluate_script와 최신cookieStoreAPI(async () => await cookieStore.getAll())를 사용합니다.
세션 전략: 라이브 탭 vs 고립된 컨텍스트
상태 오염(예: 잔류 분석 또는 인증 토큰)을 방지하려면 적절한 세션 환경을 선택하세요:
| 전략 | 사용 시기 | 설정 / 해제 |
|---|---|---|
| 라이브 탭 (활성 페이지) | 활성 사용자 세션, 실시간 401/403 오류 또는 현재 상태를 진단할 때. | 현재 선택된 페이지에서 직접 작동합니다. |
깨끗한 상태 (isolatedContext) | 쿠키 동의 배너, 첫 방문 또는 제로 쿠키 보장을 테스트할 때. | 고유한 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(...) | 해당 없음 |
| HttpOnly 수정 / 삭제 | ❌ JavaScript에서 자동 실패 | ✅ 깨끗한 상태를 위해 new_page(isolatedContext: ...) 사용 |
[!WARNING] JavaScript(
cookieStore.delete또는document.cookie = "...; max-age=0")를 통해HttpOnly쿠키를 지우려고 하면 자동으로 실패합니다. 인증되지 않았거나 새로운 상태에서 테스트하려면 항상isolatedContext와 함께new_page를 사용하여 새로운 고립된 컨텍스트를 생성하세요.
워크플로우 패턴
1. 인증 오류 및 리디렉션 진단 (401 / 403)
인증된 페이지 요청이 실패하거나, 401/403을 반환하거나, 로그인 페이지로 리디렉션되는 경우:
- 최근 요청 나열:
includePreservedRequests: true와 함께list_network_requests를 호출합니다. - 대상 요청 찾기: 실패한 요청(401/403) 또는 리디렉션(302/307)을 찾습니다.
- 나가는
Cookie헤더 검사:reqid와 함께get_network_request를 호출합니다.Cookie헤더가 첨부되었는지, 필수 토큰(예:SESSION_ID,auth_token)이 전송되었는지 확인합니다.
- 능동적 검사 실행 (최근 요청이 없는 경우):
- 이전 세션에서 쿠키가 설정되었고 네트워크 호출이 나열되지 않은 경우, 요청을 트리거합니다:
reload: true와 함께navigate_page를 사용하거나,() => fetch(window.location.href)와 함께evaluate_script를 호출합니다.
- 그런 다음 새 요청에 대해
get_network_request를 호출하여 활성Cookie헤더를 검사합니다.
- 이전 세션에서 쿠키가 설정되었고 네트워크 호출이 나열되지 않은 경우, 요청을 트리거합니다:
- 설정 요청 추적: 쿠키가 없거나 거부된 경우:
- 이전 로그인/핸드셰이크 응답에서
Set-Cookie지시문을 확인합니다:- 경로 불일치: 예:
Path=/api인데 요청이/로 가는 경우. - 도메인 불일치: 예:
Domain=api.example.com으로 인해sub.example.com의 쿠키가 차단되는 경우. - HTTP에서 Secure 플래그:
Secure쿠키는 암호화되지 않은http://를 통해 절대 전송되지 않습니다. - SameSite 차단:
SameSite=Strict쿠키는 사이트 간 탐색에서 생략됩니다. - 만료:
Expires또는Max-Age가 경과했는지 확인합니다.
- 경로 불일치: 예:
- 이전 로그인/핸드셰이크 응답에서
2. 쿠키 배너 및 동의 적합성 테스트
동의 전이나 거부 시 필수가 아니거나 추적하는 쿠키가 설정되지 않았는지 확인하려면:
- 깨끗하게 시작: 전용 이름으로 새로운 고립된 컨텍스트를 엽니다:
{"url": "<PAGE_URL>", "isolatedContext": "consent-test-1"} - 기준 쿠키 기록: 배너와 상호작용하기 전에
evaluate_script와async () => await cookieStore.getAll()을 실행합니다. - 조기 네트워크 요청 및 문제 검사:
list_network_requests를 호출하여 동의 전에 타사 추적 비콘이 실행되지 않았는지 확인합니다.types: ["issue"]와 함께list_console_messages를 호출하여 추적 경고가 있는지 확인합니다.
- 동의 배너와 상호작용:
take_snapshot으로 스냅샷을 캡처하여 "거부" 또는 "모두 거부" 버튼uid를 찾습니다.click으로 버튼을 클릭합니다.
- 쿠키 차이 확인:
- 클릭 후
evaluate_script와async () => await cookieStore.getAll()을 실행하여 엄격히 필요한 쿠키나 동의 상태 쿠키만 존재하는지 확인합니다.
- 클릭 후
- 동의 철회 테스트 (수명 주기 감사):
- 동의 철회 또는 기본 설정 변경을 감사할 때:
- "쿠키 설정", "기본 설정 관리" 또는 바닥글 개인정보 보호 트리거를 찾아 클릭합니다(
take_snapshot$ ightarrow$click). - 필수가 아닌 카테고리를 선택 해제하거나 "모두 철회" / "기본 설정 저장"을 클릭합니다.
cookieStore.getAll()을 다시 쿼리하여 이전에 수락한 필수가 아닌 쿠키가 지워졌거나 만료되었는지 확인합니다.- 후속 작업에서
list_network_requests를 호출하여 추적 비콘이 더 이상 실행되지 않는지 확인합니다.
- "쿠키 설정", "기본 설정 관리" 또는 바닥글 개인정보 보호 트리거를 찾아 클릭합니다(
- 동의 철회 또는 기본 설정 변경을 감사할 때:
- 컨텍스트 정리: 감사가 완료되면
close_page를 호출하여 남은 쿠키가 후속 작업에 영향을 미치지 않도록 합니다.
3. 쿠키 보안, SameSite 및 CHIPS (Partitioned Cookies) 감사
- 빠른 경로: 기본 DevTools 문제 (권장):
list_console_messages를 호출할 때 다음을 사용합니다:{ "types": ["issue"], "includePreservedMessages": true }CookieIssue항목을 확인합니다. 예:SameSiteNoneInsecure:Secure없이SameSite=None.ThirdPartyCookiePhaseout: 타사 쿠키가 차단되거나 제한됨.SchemefulSameSite: 교차 체계 쿠키 문제.PartitionedCookies: 잘못된 CHIPS 파티션 속성.
- 심층 감사: Lighthouse 타사 쿠키:
mode: "navigation"및outputDirPath: "/tmp/lh-report"와 함께lighthouse_audit를 실행합니다.- 전체 보고서를 컨텍스트에 로드하지 않고 특정 쿠키 감사를 추출합니다:
node -e "const r=require('/tmp/lh-report/report.json'); const a=r.audits['third-party-cookies']; console.log(JSON.stringify({score: a?.score, display