Web Cookie 🍪


BACKEND
NESTJS

Cookie

쿠키는 웹사이트가 사용자의 브라우저에 저장하는 작은 데이터 조각으로, 사용자 정보를 저장하고 관리하는 데 사용됩니다.

더불어 서버와 데이터를 주고 받기 위하여도 사용되지요.

주요 용도

로그인 유지

로그인 정보를 저장하여 사용자가 다시 로그인하지 않도록 유지합니다.

예를 들어, 로그인을 하면 백엔드로부터 사용자 정보가 담긴 토큰을 전달받습니다. 보통 로그인이 있는 사이트의 경우, 많은 API들이 토큰이 필요한 경우가 많죠.


로컬 스토리지나 쿠키에 토큰을 저장하지않는다면 토큰 정보가 페이지를 이동할 때마다 유실될 것입니다.(예를 들어, 새로고침을 하면 유실될 수 있죠.) 뿐만 아니라, 특정 웹을 종료하였다가 조금 후에 다시 들어가도 로그인을 해야할 것입니다.

이로 인해 , 유저는 페이지를 변경하거나 잠깐 페이지를 종료했다가 다시 접속을 하면 로그인 창으로 다시 리다이렉트될 것입니다.


이러한 불편함을 방지하기위해 웹에서는 로컬스토리지나 쿠키와 같은 데이터저장소를 제공합니다.

쿠키에 로그인 정보를 저장하면 새로고침을 하거나 나갔다가 다시 들어와도 로그인 정보가 유실되지않죠.


이렇게 쿠키는 임시적인 데이터 저장소로 사용되곤 합니다.

속성

이름(Key)

  • 쿠키의 키(key) 값입니다.

  • 예시: access_token, refresh_token

  • access_token은 사용자의 인증을 위한 JWT(JSON Web Token)이며, refresh_token은 액세스 토큰이 만료되었을 때 새로운 토큰을 발급하는 용도로 사용됩니다.

값 (Value)

  • 쿠키에 저장된 데이터 값입니다.

도메인 (Domain)

  • 쿠키가 유효한 도메인을 나타냅니다.

  • 예시: devlounge.com

  • 해당 도메인에서만 이 쿠키가 사용됩니다.

    • devlounge.com => api.devloun.com ❌

  • 서브 도메인도 해당 쿠키를 사용가능합니다.

    • devlounge.com => api.devlounge.com ✅

경로 (Path)

  • 쿠키가 유효한 URL 경로입니다.

  • 예시:

    • /concept → 이 쿠키는 /concept 하위 경로에서만 유효함.

    • / → 이 쿠키는 전체 사이트에서 사용 가능함.

만료 시간 (Expires / Max-Age)

  • 쿠키의 유효 기간을 의미합니다.

  • 세션(Session) 쿠키로 설정된 경우 브라우저를 닫으면 삭제됩니다.

  • 만약 Expires 또는 Max-Age 값이 지정되면 해당 기간 동안 유지됩니다.

크기 (Size)

  • 쿠키의 크기를 바이트 단위로 표시합니다.

  • 예시:

    173, 186 바이트

  • 브라우저는 도메인당 최대 4096바이트(4KB)까지 쿠키 저장을 허용합니다.

HttpOnly

  • HttpOnly 속성이 있으면 JavaScript에서 접근할 수 없습니다.

  • 이 속성이 없으면 document.cookie를 통해 접근 가능하므로 보안 위험이 있습니다.

  • 현재 쿠키 목록에서는 HttpOnly 속성이 설정되지 않음.

Secure

  • Secure 속성이 설정된 경우 HTTPS 환경에서만 전송됨.

  • 일반 HTTP 환경에서는 해당 쿠키가 전송되지 않음.

SameSite

크로스사이트 요청에서 쿠키 전송을 제한하는 속성입니다.

예를 들어, a.com의 쿠키가 의도치않게 b.com으로 전송되지않도록 방지하기 위한 보안성을 위한 속성이죠.

  • 값의 의미:

    • Strict: 같은 사이트에서만 쿠키를 전송.

      • devlounge.com => api.thedevlounge.com => ✅

    • Lax: 안전한 요청(GET)에서는 전송되지만, 다른 도메인에서 POST 요청 등을 보낼 경우 쿠키가 포함되지 않음.

      • devlounge.com (Get)=> dev.com ✅

        SameSite=Lax에서 쿠키가 전달되는 경우

      • 사용자가 a.com에서 b.com으로 이동하는 링크를 클릭했을 때(top-level navigation) → 즉, a.com에 있던 사용자가 b.com으로 이동하면서 GET 요청을 보내는 경우 쿠키가 전달됨.

             <a href="https://b.com">b.com으로 이동</a>

    a.com에서 b.com으로 리디렉션 되는 경우

    - 302 리디렉션이 발생해도 SameSite=Lax 쿠키는 유지됨.


    SameSite=Lax에서 쿠키가 전달되지 않는 경우

    - Ajax 요청(XHR, Fetch API)

    • fetch("https://b.com/api", { method: "GET", credentials: "include" });

      - 스크립트, 이미지, iframe 요청

    • <script src="https://b.com/script.js"></script> <img src="https://b.com/image.png">

    • None: 크로스사이트 요청에도 쿠키가 항상 전송됨(이 경우 Secure 필수).

Partition Key Site & Cross Site

  • Partition Key Site: 브라우저가 사이트별로 쿠키를 분리하는 기능.

  • Cross Site: 크로스사이트 쿠키 설정 여부를 나타냄.

우선순위 (Priority)

  • 쿠키가 제거될 가능성(우선순위)을 결정함.

  • 값의 의미:

    • High: 중요한 쿠키로, 브라우저가 가능한 한 삭제하지 않음.

    • Medium: 일반적인 쿠키.

    • Low: 브라우저가 저장 공간이 부족하면 먼저 삭제할 가능성이 있음.

⚠️ 주의사항

React에서는 Http-Only 설정 불가!

React는 즉 서버가 아닌 클라이언트에서 실행되는 앱인 경우에는 Http-Only 속성을 설정하는 것이 불가합니다. 이 속성은 서버에서만 설정가능합니다.


React 환경

     const response =  await this.request<LoginResponse>("auth/login", {
                method: "POST",
                headers: {
                    Authorization: BasicToken,
                },
            },)

            const {
                accessToken,
                refreshToken
            } =response.data

            const decodeACT = jwtDecode<Token>(accessToken)
            const decodeRFT = jwtDecode<Token>(refreshToken)

            this.cookies.set("access_token",accessToken,{
                path:"/",
                secure:true,
                sameSite: "strict",
                expires:new Date(decodeACT.exp*1000),
            })

            this.cookies.set("refresh_token",refreshToken,
                {
                    path:"/",
                    secure:true,
                    sameSite: "strict",
                    expires: new Date(decodeRFT.exp*1000),
                    httpOnly:true
                }
                )

위 코드는 react-cookie 패키지를 사용하여 쿠키를 클라이언트단에서 설정합니다.

access-token은 httpOnly 설정을 하지않고 refresh-token만 httpOnly만 설정하고 있습니다.


하지만 위 코드를 작동시키면 access-token만 브라우저에 저장되고 refresh-token은 브라우저에 저장되지 않습니다.

http-only : true는 클라이언트단에서 적용할 수 없는데 설정하려니 쿠키가 저장이 안되는 것이죠.

Next.js에서는 서버에서 실행되기 때문에 쿠키설정시 Http-Only로 설정가능하고 Http-Only로 설정된 쿠키도 읽어올 수 있습니다.