__Host- セッションクッキーを発行し、正しく消す
한국어 원문으로 표시합니다.
목표
127.0.0.1:8301 에 로그인 서버를 세워 __Host-sid 세션 쿠키를 발급·검사·폐기하고, 쿠키가 브라우저(curl) 저장소에 실제로 받아들여지고 지워지는지까지 확인한다.
왜 중요한가
쿠키 문제는 서버 로그로는 보이지 않습니다. 서버는 Set-Cookie 를 정상으로 내보냈는데 브라우저가 접두 규칙이나 Secure 조건에 걸려 조용히 버리면, 증상은 "가끔 로그인이 안 된다" 로만 나타납니다. 그래서 이 실습은 응답 헤더와 함께 curl 쿠키 저장소를 봅니다. curl 8.5 도 __Host-·__Secure- 규칙을 어긴 쿠키를 저장하지 않으므로, 저장소에 남았는지가 곧 받아들여졌는지의 근사입니다. 로그아웃도 같은 눈으로 봅니다. 쿠키를 지우는 것은 브라우저에 하는 부탁일 뿐이고, 값을 베껴 둔 사람에게는 서버 기록이 지워졌는지만 의미가 있습니다.
단계
/root/st/cookie/app.py를 127.0.0.1:8301 에 띄운다.GET /healthz가 200 과{"ok":true}를 준다.POST /login에 JSON{"user":"alice","password":"wonderland"}를 보내면 200 과{"user":"alice"}를 주고__Host-sid쿠키를 발급한다. 값은secrets로 만든 128비트 이상 무작위(base64url 22자 이상)이고 로그인할 때마다 달라야 하며, 사용자 이름이 들어가면 안 된다.bob/builder도 로그인된다.- 세션 쿠키에
Secure,HttpOnly,SameSite=Lax,Path=/를 붙이고Domain과Max-Age/Expires는 붙이지 않는다(호스트 전용 세션 쿠키). alice 로 로그인하며curl -c /root/st/cookie/jar.txt로 쿠키 저장소를 남긴다. 그 파일의__Host-sid줄은#HttpOnly_127.0.0.1로 시작하고 경로/, SecureTRUE, 만료0이어야 한다. GET /me는 유효한__Host-sid가 있으면 200 과{"user":"<사용자>"}를 주고, 쿠키가 없거나 서버가 발급한 적 없는 값이면 401 을 준다.- 틀린 비밀번호(
alice/wrongpass)와 없는 사용자(mallory)의POST /login은 401 이고Set-Cookie가 한 줄도 없어야 한다. POST /logout은 200 을 주고 서버 쪽 세션을 지운다. 응답에는__Host-sid를 지우는Set-Cookie(Max-Age=0)가 있어야 하고, curl 저장소가 그 삭제를 실제로 받아들여 쿠키가 사라져야 한다. 로그아웃 전에 복사해 둔 쿠키 값으로GET /me를 부르면 401 이어야 한다. 3번의jar.txt는 덮어쓰지 말고 다른 저장소 파일로 시험한다.https://app.example.com/login의 응답으로 아래 Set-Cookie 값이 왔을 때 브라우저가 쿠키를 저장하는지 판정해/root/st/cookie/prefix.txt에번호=accept또는번호=reject로 한 줄씩 적는다.- 1
__Host-sid=a1; Secure; HttpOnly; Path=/; SameSite=Lax - 2
__Host-sid=a2; HttpOnly; Path=/; SameSite=Lax - 3
__Host-sid=a3; Secure; Path=/; Domain=app.example.com - 4
__Host-sid=a4; Secure; Path=/account - 5
__Secure-theme=dark; Secure; Path=/account; Domain=example.com - 6
__Secure-theme=dark; Path=/ - 7
__host-sid=a7; Path=/ - 8
theme=dark; Path=/; Domain=example.com
- 1
/root/st/cookie/e2e.sh로 로그인, 쿠키로 /me, 쿠키 없이 /me, 틀린 비밀번호, 로그아웃, 옛 쿠키 재사용을 차례로 시험하고, 로그아웃 뒤 저장소에 남은__Host-sid줄 수를 세어/root/st/cookie/e2e.out에login=200 me=200 no_cookie=401 bad_password=401 logout=200 replay=401 jar_after_logout=0을 한 줄로 적는다.
참고
- 응답 헤더 보기:
curl -s -D - -o /dev/null ...저장소 남기기·쓰기:curl -c <파일> -b <파일> ... - curl 저장소 파일은 탭으로 나뉜 일곱 열입니다.
awk -F'\t'로 열을 꺼낼 수 있습니다. - 이 파드의 curl 8.5 는
http://127.0.0.1로 받은 Secure 쿠키도 저장하고 되돌려 보냅니다. 실제 브라우저에서는 HTTPS 가 필요합니다(localhost 는 예외). - 흔한 실수 1: 로그아웃에서 쿠키만 지우고 서버 세션을 남기는 것 — 베껴 둔 값이 계속 통합니다.
- 흔한 실수 2: 삭제 쿠키에
Secure나Path=/를 빠뜨리는 것 —__Host-규칙에 걸려 삭제 자체가 무시되고 옛 쿠키가 남습니다.
로그인 서버 띄우기
/root/st/cookie/app.py 를 127.0.0.1:8301 에 띄운다. GET /healthz 가 200 과 {"ok":true} 를 준다.
표준 라이브러리 http.server 로 충분합니다. 서버는 백그라운드로 띄우고 /healthz 가 답할 때까지 기다렸다가 다음으로 넘어가세요.
로그인에 세션 쿠키 발급하기
POST /login 에 JSON {"user":"alice","password":"wonderland"} 를 보내면 200 과 {"user":"alice"} 를 주고 __Host-sid 쿠키를 발급한다. 값은 secrets 로 만든 128비트 이상 무작위(base64url 22자 이상)이고 로그인할 때마다 달라야 하며, 사용자 이름이 들어가면 안 된다. bob/builder 도 로그인된다.
세션 값은 의미를 담지 않는 열쇠입니다. random 모듈은 예측할 수 있으니 암호학적 난수를 쓰는 secrets 모듈에서 URL 에 안전한 문자열을 만드는 함수를 찾아보세요. 누구의 세션인지는 서버가 dict 에 기록합니다.
쿠키 속성과 저장소 확인하기
세션 쿠키에 Secure, HttpOnly, SameSite=Lax, Path=/ 를 붙이고 Domain 과 Max-Age/Expires 는 붙이지 않는다(호스트 전용 세션 쿠키). alice 로 로그인하며 curl -c /root/st/cookie/jar.txt 로 쿠키 저장소를 남긴다. 그 파일의 __Host-sid 줄은 #HttpOnly_127.0.0.1 로 시작하고 경로 /, Secure TRUE, 만료 0 이어야 한다.
Domain 은 적을수록 넓어집니다. 생략해야 보낸 호스트에만 갑니다. 만료를 적지 않으면 브라우저를 닫을 때 사라지는 세션 쿠키가 됩니다. curl 저장소 파일은 탭으로 나뉜 일곱 열이고, HttpOnly 쿠키는 첫 열 앞에 표시가 붙습니다.
쿠키로 사용자 알아보기
GET /me 는 유효한 __Host-sid 가 있으면 200 과 {"user":"<사용자>"} 를 주고, 쿠키가 없거나 서버가 발급한 적 없는 값이면 401 을 준다.
쿠키가 붙어 왔다는 사실만으로는 아무것도 증명되지 않습니다. 그 값으로 서버의 세션 기록을 찾아 사용자를 꺼내야 합니다.
실패한 로그인에는 쿠키를 주지 않기
틀린 비밀번호(alice/wrongpass)와 없는 사용자(mallory)의 POST /login 은 401 이고 Set-Cookie 가 한 줄도 없어야 한다.
쿠키를 만드는 코드가 비밀번호 검사보다 앞에 있으면 실패에도 쿠키가 나갑니다. 비밀번호 비교는 시간 차가 새지 않는 함수를 쓰는 편이 좋습니다.
로그아웃을 진짜로 만들기
POST /logout 은 200 을 주고 서버 쪽 세션을 지운다. 응답에는 __Host-sid 를 지우는 Set-Cookie(Max-Age=0)가 있어야 하고, curl 저장소가 그 삭제를 실제로 받아들여 쿠키가 사라져야 한다. 로그아웃 전에 복사해 둔 쿠키 값으로 GET /me 를 부르면 401 이어야 한다. 3번의 jar.txt 는 덮어쓰지 말고 다른 저장소 파일로 시험한다.
쿠키 삭제는 부탁일 뿐입니다. 값을 베껴 둔 사람에게는 서버 기록이 지워졌는지만 의미가 있습니다. 그리고 삭제 쿠키도 쿠키라서 __Host- 접두의 조건을 다시 통과해야 브라우저가 받아들입니다.
접두 규칙 판정하기
https://app.example.com/login 의 응답으로 아래 Set-Cookie 값이 왔을 때 브라우저가 쿠키를 저장하는지 판정해 /root/st/cookie/prefix.txt 에 번호=accept 또는 번호=reject 로 한 줄씩 적는다. 1 __Host-sid=a1; Secure; HttpOnly; Path=/; SameSite=Lax · 2 __Host-sid=a2; HttpOnly; Path=/; SameSite=Lax · 3 __Host-sid=a3; Secure; Path=/; Domain=app.example.com · 4 __Host-sid=a4; Secure; Path=/account · 5 __Secure-theme=dark; Secure; Path=/account; Domain=example.com · 6 __Secure-theme=dark; Path=/ · 7 __host-sid=a7; Path=/ · 8 theme=dark; Path=/; Domain=example.com
접두마다 요구하는 조건 목록을 먼저 적어 두고 한 줄씩 대조하세요. __Host- 는 세 가지, __Secure- 는 한 가지입니다. 브라우저가 접두를 대소문자를 구분해 대조하는지도 규격에서 확인해 보세요. # 으로 시작하는 줄은 주석으로 둘 수 있습니다.
전체 흐름을 한 번에 증명하기
/root/st/cookie/e2e.sh 로 로그인, 쿠키로 /me, 쿠키 없이 /me, 틀린 비밀번호, 로그아웃, 옛 쿠키 재사용을 차례로 시험하고, 로그아웃 뒤 저장소에 남은 __Host-sid 줄 수를 세어 /root/st/cookie/e2e.out 에 login=200 me=200 no_cookie=401 bad_password=401 logout=200 replay=401 jar_after_logout=0 을 한 줄로 적는다.
앞 스텝의 확인을 하나의 쿠키 저장소로 이어서 돌리면 됩니다. 재사용 시험에 쓸 옛 값은 로그아웃하기 전에 저장소에서 꺼내 두어야 합니다.