SameSite 쿠키 레시피

Chrome, Firefox, Edge 등은 IETF 제안인 점진적으로 더 나은 쿠키에 따라 기본 동작을 다음과 같이 변경합니다.

  • SameSite 속성이 없는 쿠키는 SameSite=Lax로 취급됩니다. 즉, 기본 동작은 퍼스트 파티 컨텍스트로만 쿠키를 제한하는 것입니다.
  • 크로스 사이트 사용용 쿠키는 서드 파티 컨텍스트에 포함할 수 있도록 SameSite=None; Secure를 지정해야 합니다.

이 기능은 Chrome 84 공개 버전 이상의 기본 동작입니다. 아직 업데이트하지 않았다면 앞으로 차단되지 않도록 서드 파티 쿠키의 속성을 업데이트해야 합니다.

교차 브라우저 지원

MDN의 Set-Cookie 페이지에 있는 브라우저 호환성 섹션을 참고하세요.

크로스 사이트 또는 서드 파티 쿠키의 사용 사례

서드 파티 컨텍스트로 쿠키를 전송해야 하는 경우 여러 가지 일반적인 사용 사례와 패턴이 있습니다. 이러한 사용 사례 중 하나를 제공하거나 의존하는 경우 서비스가 계속 올바르게 작동하도록 귀하 또는 제공업체가 쿠키를 업데이트해야 합니다.

<iframe> 내의 콘텐츠

<iframe>에 표시된 다른 사이트의 콘텐츠는 서드 파티 맥락의 콘텐츠입니다. 표준 사용 사례는 다음과 같습니다.

  • 다른 사이트에서 공유한 삽입된 콘텐츠(예: 동영상, 지도, 코드 샘플, 소셜 게시물)
  • 결제, 캘린더, 예약, 예약 기능과 같은 외부 서비스의 위젯
  • <iframes>를 덜 명확하게 만드는 소셜 버튼 또는 사기 방지 서비스와 같은 위젯

여기에서 쿠키를 사용하여 세션 상태를 유지하거나, 일반 환경설정을 저장하거나, 통계를 사용 설정하거나, 기존 계정 사용자를 위한 콘텐츠를 맞춤설정할 수 있습니다.

삽입된 콘텐츠의 URL이 페이지 URL과 일치하지 않는 브라우저 창 다이어그램
삽입된 콘텐츠가 최상위 탐색 컨텍스트와 동일한 사이트에서 제공되지 않으면 서드 파티 콘텐츠입니다.

또한 웹은 기본적으로 구성 가능하므로 최상위 또는 퍼스트 파티 컨텍스트에서도 표시되는 콘텐츠를 삽입하는 데 <iframes>를 사용합니다. 해당 사이트에서 사용하는 쿠키는 사이트가 프레임 내에 표시될 때 서드 파티 쿠키로 간주됩니다. 쿠키를 사용하여 작동하면서 다른 사용자가 쉽게 삽입할 수 있도록 하려는 사이트를 만드는 경우 이러한 사이트가 크로스 사이트 사용으로 표시되는지 확인하거나 쿠키를 사용하지 않고 적절하게 대체할 수 있어야 합니다.

사이트 전반에 걸친 '안전하지 않은' 요청

여기서는 '안전하지 않음'이 다소 우려스럽게 들릴 수 있지만, 이는 상태를 변경하기 위한 모든 요청을 의미합니다. 웹에서는 주로 POST 요청입니다. SameSite=Lax로 표시된 쿠키는 안전한 최상위 수준 탐색(예: 링크를 클릭하여 다른 사이트로 이동)을 통해 전송됩니다. 그러나 POST를 통해 다른 사이트에 제출하는 <form> 등의 항목에는 쿠키가 포함되지 않습니다.

한 페이지에서 다른 페이지로 이동하는 요청의 다이어그램
수신 요청이 '안전' 메서드를 사용하면 쿠키가 전송됩니다.

이 패턴은 재방문 전에 일부 작업(예: 서드 파티 ID 공급업체로 리디렉션)을 실행하기 위해 사용자를 원격 서비스로 리디렉션할 수 있는 사이트에 사용됩니다. 사용자가 사이트를 떠나기 전에 일회용 토큰이 포함된 쿠키가 설정됩니다. 이때 반환 요청에서 이 토큰을 확인하여 교차 사이트 요청 위조 (CSRF) 공격을 완화할 수 있습니다. 반환 요청이 POST를 통해 들어오는 경우 쿠키를 SameSite=None; Secure로 표시해야 합니다.

원격 리소스

페이지의 모든 원격 리소스는 <img> 태그, <script> 태그 등에서 요청과 함께 전송되는 쿠키를 사용할 수 있습니다. 일반적인 사용 사례로는 추적 픽셀과 콘텐츠 맞춤설정이 있습니다.

이는 fetch 또는 XMLHttpRequest에 의해 JavaScript에서 시작된 요청에도 적용됩니다. fetch()credentials: 'include' 옵션과 함께 호출되면 이러한 요청에서 쿠키가 잘 작동할 것이라고 예상할 수 있습니다. XMLHttpRequest의 경우 true로 설정된 withCredentials 속성의 인스턴스를 찾아야 합니다. 이는 이러한 요청에서 쿠키가 잘 사용될 수 있음을 나타냅니다. 이러한 쿠키는 크로스 사이트 요청에 포함되도록 적절하게 표시되어야 합니다.

WebView 내 콘텐츠

플랫폼별 앱의 WebView는 브라우저에서 구동되므로 동일한 제한사항이나 문제가 적용되는지 테스트해야 합니다. Android에서 WebView를 Chrome에서 제공하는 경우 Chrome 84에서는 새 기본값이 즉시 적용되지 않습니다. 그러나 향후에 이를 적용하는 것이 목적이므로 여전히 이를 테스트하고 준비해야 합니다. 또한 Android는 플랫폼별 앱이 CookieManager API를 통해 직접 쿠키를 설정하도록 허용합니다. 헤더 또는 JavaScript를 통해 설정된 쿠키와 마찬가지로 크로스 사이트 사용을 위한 SameSite=None; Secure를 포함하는 것이 좋습니다.

지금 바로 SameSite를 구현하는 방법

퍼스트 파티 컨텍스트에서만 필요한 쿠키의 경우 필요에 따라 SameSite=Lax 또는 SameSite=Strict로 표시하는 것이 이상적입니다. 아무 조치도 취하지 않고 브라우저에서 기본값을 적용하도록 허용할 수도 있지만 이 경우 브라우저에서 일관되지 않은 동작이 발생할 위험이 있고 각 쿠키에 대해 콘솔 경고가 발생할 수 있습니다.

Set-Cookie: first_party_var=value; SameSite=Lax

서드 파티 컨텍스트에 필요한 쿠키의 경우 SameSite=None; Secure로 표시되어야 합니다. 두 속성을 함께 사용해야 합니다. Secure 없이 None만 지정하면 쿠키가 거부됩니다. 하지만 브라우저 구현에는 상호 호환되지 않는 차이점이 있으므로 아래의 호환되지 않는 클라이언트 처리에 설명된 완화 전략을 사용해야 할 수도 있습니다.

Set-Cookie: third_party_var=value; SameSite=None; Secure

호환되지 않는 클라이언트 처리

None를 포함하고 기본 동작을 업데이트하기 위한 이러한 변경사항은 아직 비교적 새로운 것이기 때문에 브라우저 간에 이러한 변경사항이 처리되는 방식이 일치하지 않습니다. 현재 알려진 문제는 chromium.org의 업데이트 페이지를 참고할 수 있지만 전체 문제인지는 알 수 없습니다. 이는 이상적이지 않지만 이 전환 단계에서 채택할 수 있는 해결 방법이 있습니다. 하지만 일반적인 규칙은 호환되지 않는 클라이언트를 특별한 경우로 취급하는 것입니다. 최신 규칙을 구현하는 브라우저에 대한 예외를 생성하면 안 됩니다.

첫 번째 옵션은 새 스타일과 이전 스타일 쿠키를 모두 설정하는 것입니다.

Set-cookie: 3pcookie=value; SameSite=None; Secure
Set-cookie: 3pcookie-legacy=value; Secure

최신 동작을 구현하는 브라우저는 SameSite 값으로 쿠키를 설정하지만 다른 브라우저는 쿠키를 무시하거나 잘못 설정할 수 있습니다. 그러나 동일한 브라우저에서는 3pcookie-legacy 쿠키를 설정합니다. 포함된 쿠키를 처리할 때 사이트에서는 먼저 새로운 스타일의 쿠키가 있는지 확인하고 찾을 수 없는 경우 기존 쿠키로 대체해야 합니다.

아래 예시에서는 Node.js에서 Express 프레임워크cookie-parser 미들웨어를 사용하여 이 작업을 수행하는 방법을 보여줍니다.

const express = require('express');
const cp = require('cookie-parser');
const app = express();
app.use(cp());

app.get('/set', (req, res) => {
  // Set the new style cookie
  res.cookie('3pcookie', 'value', { sameSite: 'none', secure: true });
  // And set the same value in the legacy cookie
  res.cookie('3pcookie-legacy', 'value', { secure: true });
  res.end();
});

app.get('/', (req, res) => {
  let cookieVal = null;

  if (req.cookies['3pcookie']) {
    // check the new style cookie first
    cookieVal = req.cookies['3pcookie'];
  } else if (req.cookies['3pcookie-legacy']) {
    // otherwise fall back to the legacy cookie
    cookieVal = req.cookies['3pcookie-legacy'];
  }

  res.end();
});

app.listen(process.env.PORT);

단점은 중복 쿠키를 설정하여 모든 브라우저를 포괄하므로 쿠키를 설정하고 읽을 때 모두 변경해야 한다는 것입니다. 그러나 이 접근 방식은 동작과 관계없이 모든 브라우저를 포괄하고 서드 파티 쿠키가 이전처럼 계속 작동하도록 해야 합니다.

또는 Set-Cookie 헤더를 전송하는 시점에 사용자 에이전트 문자열을 통해 클라이언트를 감지하도록 선택할 수 있습니다. 호환되지 않는 클라이언트 목록을 참조한 다음 플랫폼에 적합한 라이브러리(예: Node.js의 ua-parser-js 라이브러리)를 사용합니다. 정규 표현식을 직접 작성하고 싶지 않을 가능성이 높으므로 사용자 에이전트 감지를 처리하는 라이브러리를 찾는 것이 좋습니다.

이 접근 방식의 장점은 쿠키를 설정하는 시점에 한 번만 변경하면 된다는 것입니다. 하지만 여기서 필요한 경고는 사용자 에이전트 스니핑이 근본적으로 취약하며 영향을 받는 모든 사용자를 포착하지 못할 수 있다는 것입니다.

언어, 라이브러리, 프레임워크에서 SameSite=None 지원

대부분의 언어 및 라이브러리는 쿠키의 SameSite 속성을 지원하지만 SameSite=None가 추가된 것은 아직 비교적 새로운 기능이므로 당분간 일부 표준 동작을 해결해야 할 수도 있습니다. 이러한 내용은 GitHub의 SameSite 예시 저장소에 나와 있습니다.

지원 받기

쿠키는 도처에 있으며 쿠키가 설정되고 사용되는 위치를 완전히 감사하는 사이트는 드뭅니다. 특히 크로스 사이트 사용 사례가 혼합된 경우에는 더욱 그렇습니다. 문제가 발생하면 누구나 처음 경험한 것일 수 있으므로 주저하지 말고 문의하세요.