본문 바로가기
MAKESITE
무료 스캔 문의하기 로그인

점검 항목 · 보안

쿠키 보안 속성 누락

세 속성은 각각 다른 사고를 막습니다. 하나가 빠지면 정확히 그 구멍만 열려 있는 것이고, 그래서 뭉뚱그려 볼 수 없습니다.

심각도 높음 보안 무료 스캔 판정 항목 /check/cookie-flags

01DEFINITION

한 줄로 말하면

세션 쿠키에 HttpOnly 가 없으면 자바스크립트로 읽힙니다. XSS 한 번에 계정이 그대로 넘어간다는 뜻입니다. Secure 가 없으면 http 요청에도 쿠키가 실려 나가고, SameSite 가 없으면 다른 사이트가 우리 쪽으로 보내는 요청에도 쿠키가 따라붙습니다(CSRF). 세 속성은 각각 다른 사고를 막기 때문에, 하나가 빠지면 정확히 그 구멍만 열려 있습니다.

02RISK

없으면 무슨 일이 생기나

HttpOnly 가 빠진 경우가 가장 직접적입니다. 페이지 어딘가에 스크립트가 한 줄 끼어들면 그 스크립트가 세션 쿠키를 읽어 외부로 보낼 수 있습니다. 공격자는 그 값으로 우리 사이트에 그 사용자로 접속합니다. 비밀번호를 몰라도 되고, 이중 인증을 켜 두었어도 이미 인증된 세션이라 그대로 통과합니다.

Secure 가 빠지면 쿠키가 암호화되지 않은 요청에도 실려 나갑니다. https 로만 서비스한다고 생각해도, 방문자가 주소를 http 로 한 번 치거나 옛 링크를 누르는 순간 세션 값이 평문으로 네트워크를 지나갑니다. https 강제 리다이렉트를 걸어 두었어도 그 첫 요청에는 이미 쿠키가 실려 있습니다.

SameSite 가 빠진 경우는 조금 다릅니다. 공격자가 만든 페이지에서 우리 사이트로 요청을 보낼 때 브라우저가 우리 쿠키를 함께 붙여 줍니다. 사용자는 자기 계정으로 로그인해 있으므로 그 요청은 정상 처리됩니다. 회원정보 변경이나 결제가 사용자 모르게 실행되는 CSRF 가 여기서 나옵니다.

03CHECK IT YOURSELF

직접 확인하는 방법

아래는 저희를 거치지 않고 지금 해 보실 수 있는 방법입니다. 결과를 저희에게 보내실 필요도 없습니다.

  1. 01
    로그인 후 설정되는 쿠키를 본다

    중요한 것은 세션 쿠키입니다. 로그인 요청의 응답에서 Set-Cookie 줄을 확인하십시오. 로그인 전 홈에서 보이는 쿠키만 확인하면 정작 세션 쿠키를 못 봅니다.

    $ curl -sI https://example.com/ | grep -i set-cookie
    set-cookie: PHPSESSID=abc123; path=/; HttpOnly; Secure; SameSite=Lax
  2. 02
    브라우저에서 세 칸을 확인한다

    개발자도구의 애플리케이션 탭에서 쿠키 목록을 열면 HttpOnly, Secure, SameSite 가 각각 열로 나옵니다. 세션 쿠키 한 줄만 보시면 됩니다.

  3. 03
    스크립트로 읽히는지 직접 해 본다

    콘솔에서 아래를 실행했을 때 세션 쿠키 이름이 목록에 나오면 HttpOnly 가 빠진 것입니다. 가장 확실한 확인 방법입니다.

    > document.cookie

04HOW TO FIX

고치는 방법

서버 환경에 따라 적을 자리가 다릅니다. 아래는 가장 흔한 구성 기준이고, 적용 전에 현재 설정을 백업하십시오.

PHP — 세션 쿠키 설정을 바꾼다

php.ini 를 고칠 수 있다면 그쪽이 가장 확실합니다. 세션이 시작되기 전에 적용되어야 하므로 코드에서 바꿀 때는 session_start 앞에 두어야 합니다.

session.cookie_httponly = 1
session.cookie_secure   = 1
session.cookie_samesite = Lax
php.ini 를 못 고치는 환경에서는 응답 헤더로 붙인다

공유 호스팅이나 코어 수정이 금지된 환경에서 쓰는 방법입니다. 이미 속성이 붙어 있는 쿠키는 건드리지 않도록 조건을 겁니다.

Header always edit Set-Cookie "^(?!.*[Hh]ttp[Oo]nly)(.*)$" "$1; HttpOnly"
Header always edit Set-Cookie "^(?!.*[Ss]ame[Ss]ite)(.*)$" "$1; SameSite=Lax"
Header always edit Set-Cookie "^(?!.*[Ss]ecure)(.*)$" "$1; Secure"
SameSite 값은 Lax 에서 시작한다

Strict 로 두면 외부에서 들어오는 링크를 눌렀을 때 로그인 상태가 아닌 것처럼 보입니다. 메일로 보낸 주문 조회 링크가 대표적인 피해자입니다. Lax 는 일반적인 링크 이동을 허용하면서 위험한 요청만 막습니다.

Secure 는 인증서가 준비된 다음에 켠다

https 가 아직 없는 상태에서 Secure 를 붙이면 쿠키가 아예 전달되지 않아 로그인이 되지 않습니다. 순서는 인증서, https 강제, 그다음 Secure 입니다.

주의. SameSite 를 None 으로 지정하려면 Secure 가 반드시 함께 있어야 합니다. 결제창이나 외부 위젯과 쿠키를 주고받아야 해서 None 이 필요한 경우, Secure 없이 None 만 붙이면 브라우저가 그 쿠키를 통째로 무시합니다.

05FAQ

자주 묻는 질문

HttpOnly 를 켜면 자바스크립트에서 로그인 상태를 못 읽나요

세션 쿠키를 직접 읽지 못하게 되는 것은 맞습니다. 다만 로그인 여부를 화면에 표시하는 정도는 서버가 렌더링할 때 넘겨 주거나 별도의 비민감 쿠키를 하나 더 두면 해결됩니다. 세션 값 자체를 스크립트가 읽어야 하는 정상적인 이유는 거의 없습니다.

Lax 와 Strict 중 무엇을 골라야 하나요

외부에서 링크를 타고 들어오는 방문이 있다면 Lax 입니다. Strict 는 다른 사이트에서 넘어온 모든 요청에 쿠키를 붙이지 않기 때문에, 메일이나 메신저로 보낸 링크를 눌러 들어온 사용자가 로그인되지 않은 화면을 보게 됩니다. 관리자 전용 도구처럼 외부 유입이 없는 경우에만 Strict 가 적합합니다.

쿠키가 여러 개인데 전부 이 속성을 붙여야 하나요

세션과 인증에 관련된 쿠키가 우선입니다. 화면 설정이나 최근 본 상품처럼 민감하지 않은 값까지 전부 HttpOnly 로 만들면 정상적으로 쓰던 스크립트가 동작하지 않습니다. 무엇이 인증에 쓰이는 쿠키인지 먼저 구분하십시오.

SameSite 만 켜면 CSRF 대책이 끝나나요

끝나지 않습니다. SameSite 는 브라우저가 지켜 주는 방어라서 브라우저를 거치지 않는 요청에는 적용되지 않고, 같은 사이트 안에서 시작된 요청도 막지 않습니다. 서버 쪽 토큰 검증은 그대로 있어야 하고, SameSite 는 그 위에 한 겹 더 얹는 것입니다.
여기까지는 항목 일반론입니다. 무엇이고 왜 문제이며 어떻게 고치는지는 감출 이유가 없어 그대로 적었습니다. 리포트에 들어가는 것은 귀사 사이트에서 실제로 어떤 값이 나왔는지 — 어느 주소의 어느 응답인지, 그 환경에서 어떤 순서로 적용해야 하는지, 예상 공수가 얼마인지입니다. 이 페이지만 보고 직접 고치셔도 되고, 실제로 그러시라고 적어 두었습니다.

GET STARTED

내 사이트는
이 항목을 통과할까요

주소만 넣으시면 이 항목을 포함해 25가지를 판정해 점수와 실패 개수를 보여 드립니다. 로그인도, 소유 확인도 필요 없습니다 — 전부 밖에서 관찰만 하는 항목이기 때문입니다.