Technology/Web

SOP(Same Origin Policy)의 한계와 쿠키(Cookie)의 SameSite 속성의 활용

ikjo 2024. 2. 20. 00:37
목차

1. SOP 와 SameSite 와 관련한 추억..💫

2. SOP 의 필요성
  2-1. SOP 란?
  2-2. Same Origin vs Cross Origin
  2-3. SOP 매커니즘 이해하기
  2-4. Preflight Request 란?

3. Spring boot 의 Preflight Request 처리 과정, 간단하게 살펴보기!

4. SOP 의 한계

5. 쿠키의 SameSite 속성에 대해 알아보자!
  5-1. SameSite 란?
  5-2. Same Site vs Cross Site

6. SameSite 속성별 필요성
  6-1. SameSite = None
  6-2. SameSite = Strict
  6-3. SameSite = Lax

7. 오늘날 SameSite 사용에 대한 고찰

 

1. SOP 와 SameSite 와 관련한 추억..💫

2022 마스터즈 코스를 하던 당시 팀 프로젝트를 하면서 서버와 클라이언트 간 쿠키를 주고 받았던 적이 있었다. 이때, 쿠키에는 사용자의 인가(Authorization) 데이터를 담고 있었다. 당시 프론트 엔드 개발 팀원들은 로컬 환경에서 AWS 인프라 환경에 배포된 API 서버를 호출하면서 SPA(Single Page Application) 를 개발하고있었는데, 그 유명한 CORS(Cross Origin Resource Sharing) 이슈가 발생했었다. 이는 서버 측에서 Access-Control-Allow-Origin 응답 헤더 값에 허용할 Origin 을 할당하는 등 간단한 작업으로 해결되는 부분이었다.

 

하지만, 이외에도 또 다른 이슈가 있었다. 프론트 엔드 측에서는 분명히 서버 API 를 호출 시 사용자의 인가 데이터를 담고있는 쿠키를 할당해주었다고 하는데, 서버 측에서는 해당 쿠키를 전달받지 못하고 있는 것이다. 이러한 이슈를 목격했을 당시의 웹 브라우저는 V8 엔진 기반의 크롬이었다. 이때, 서버에서 클라이언트에 쿠키를 정상적으로 응답하는지 확인하고자 개발자 도구를 열어봤더니 서버에서 응답하는 쿠키 헤더(Set-Cookie)에 들어보지도 못했었던 "SameSite=Lax" 라는 속성값이 설정되어있었다. 👀

 

 

2. SOP 의 필요성

2-1. SOP 란?

Web MDN 에 따르면 SOP 는 한 origin 으로부터 로드된 document 또는 script 가 다른 origin 의 리소스와 상호작용할 수 있는 방법을 제한하는 정책이라고 정의하고있다. 즉, SOP 는 일종의 브라우저의 보안 매커니즘인 것이다.

 

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

 

2-2. Same Origin vs Cross Origin

여기서 origin 은 서버가 제공한 document 나 script 의 출처를 나타내는데, 서버 URL 을 구성하는 scheme, host, port 로 정의된다. 네이버 서비스를 예로 들면, https://www.naver.com:443 를 하나의 origin 으로 생각할 수 있다. 실제로도 웹 브라우저를 통해 특정 웹 사이트에 접속한 후 개발자 도구를 열어 콘솔창에서 document.location.origin 명령어를 통해 본 document 의 출처를 확인할 수 있다.

 

https://developer.mozilla.org/en-US/docs/Glossary/Origin

 

즉, Same Origin Policy 에서 Same Origin 은 '동일 출처'를 나타내는 말이 되며, SOP 는 '동일 출처 정책'으로 해석해볼 수 있다. 참고로, '다른 출처'를 나타내는 말은 Cross Origin 이다. 그 유명한 CORS 에서 CO 의 의미가 Cross Origin 인 것이다.

 

2-3. SOP 매커니즘 이해하기

앞서 Web MDN 에서 SOP 를 정의한 내용에 대해 좀 더 세부적으로 파헤쳐보자.

 

"한 origin 으로부터 로드된 document 또는 script 가 다른 origin 의 리소스와 상호작용할 수 있는 방법을 제한"

 

위 내용을 좀 더 축약해서 생각해보면 다른 origin 간의 리소스 상호작용을 제한한다고 볼 수 있다. 아래 상황을 예로 들어보자.

 

 

전형적인 CORS 이슈 시나리오이다. https://www.ikjo.com 이라는 origin 으로부터 로드된 document 또는 script 에서 https://api.ikjo.com 이라는 origin 에 GET 요청을 보내는 상황이다. 이때, 브라우저의 SOP 정책에 의해 우리가 자주 목격했었던 "... blocked by CORS policy : No ..." 콘솔 로깅과 함께 서버가 응답한 리소스를 브라우저가 차단하여 클라이언트는 해당 리소스에 접근할 수 없게 된다.

 

앞서 팀 프로젝트 당시 클라이언트 측에서 서버 API 를 호출했을 때 CORS 이슈가 발생했었던 것은 이러한 브라우저의 SOP 때문인 것이다. 사실, CORS 이슈라고 불리기는 하지만, (마치 CORS 가 막아버리는 듯한 느낌이 들지만) CORS 는  Cross Origin Resource Sharing 의 약어로 HTTP 응답 헤더 Access-Control-Allow-Origin 기반으로 브라우저의 SOP 정책을 bypass 해주는 매커니즘이다. 대표적으로 Preflight Request 가 있다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

 

이때, 이러한 SOP 가 꼭 필요한 것일까 의문이 들 수 있다. 만약 SOP 가 없다면 무슨 문제가 발생할까?

 

SOP 가 없다고 가정했을 때, CSRF(Cross Site Request Forgery) 공격이 진행되는 가상의 시나리오를 대략적으로 아래와 같이 나타내보았다.

 

1. User 는 메일 서비스를 이용하고있으며, 현재 인증을 마치고 쿠키에 본인의 인가 정보를 지니고 있다.

2. Hacker 는 메일, 문자메시지 등의 채널을 통해 User 에게 악의적 공격이 담긴 가상의 웹 URL 을 노출시킨다.

3. User 는 아무것도 모른 채 가상의 웹 URL 에 접속한다.

4. User 의 브라우저에 Hacker 의 document 또는 script 파일이 로드될 때, 메일 서비스에서 제공하는 사용자 이메일 리스트를 조회하는 API 가 호출된다. 이때, 메일 서비스 서버로부터 응답받은 User 의 쿠키 데이터도 같이 전송된다.

5. 메일 서비스 서버는 User 의 인가 정보를 검증한 후 해당 API 요청을 처리하고 응답한다.

6. User 의 브라우저는 Hacker 가 구현한 document 또는 script 파일의 로직에 따라 응답받은 데이터를 Hacker 의 특정 웹 URL 로 전송한다.

 

결과적으로, User 는 어떤 웹 URL 을 클릭했을 뿐인데, 본인의 개인 정보가 누군가에게 전달되는 결과를 맞이했다. 그러나, 이때 SOP 가 있었다면 Hacker 의 origin 과 메일 서비스 서버의 origin 이 다르기에, Hacker 의 origin 으로부터 로드된 document 나 script 에서 메일 서비스 서버가 응답한 리소스에 접근하지 못하도록 브라우저 단에서 차단했을 것이다.

 

2-4. Preflight Request 란?

앞서 "메일 서비스 서버가 응답한 리소스에 접근하지 못하도록"이라고 언급을 했는데, 이는 클라이언트가 서버 측으로 보낸 요청이 어찌됐든 정상 처리가 되었다는 것이다. 정상 처리가 된 후 서버가 클라이언트에 응답까지 했다는 것이다. 이는 서버의 리소스를 변경하지 않는 단순 조회 API 를 호출할 경우에는 응답 데이터가 브라우저 단에서 차단되기에 치명적이진 않지만, 서버의 리소스를 변경하는 API 를 호출하는 경우에는 심각한 문제가 될 수 있다. 왜냐하면 Hacker 의 origin 으로부터 로드된 document 나 script 에서 서버가 응답한 데이터에 접근하지 못한다 한들 User 가 의도치 않은 서버 리소스의 변경 작업은 불가피한 것이다. 즉, SOP 가 있어도 User 는 CSRF 공격에 노출될 수 있는 것이다.

 

이러한 경우 Preflight Request 가 도움이 될 수 있다. Preflight Request 는 브라우저가 cross origin context 에서 target server 에 본 요청을 보내기 앞서 "현재 로드된 document 또는 script 의 출처가 target server 에서 허용해준 origin 인지" 확인하기 위해 target server 로 보내는 요청이다. 이때의 HTTP METHOD 는 OPTIONS 가 된다. 

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests

 

이러한 Preflight Request 를 통해 CSRF 공격으로부터 사용자의 데이터 또는 서버의 리소스를 지켜낼 수 있게 된다.

 

참고로, Preflight Request 외 Simple Request 도 있는데, 이는 Preflight Request 와 달리 cross origin context 에서도 유효한 origin 인지 검증 절차 없이 target server 에 본 요청을 바로 보낸다는 특징이 있다. 다만, target server 는 웹 브라우저로 하여금 해당 리소스에 대한 접근을 제어하고자 Access-Control-Allow-Origin 헤더를 응답해준다. 즉, CSRF 공격에 노출되어있는 것이다. 이때, 이 Simple Request 가 왜 존재하는지 의문을 가질 수 있는데, 이는 CORS 정책이 있기 전부터 있었던 form 태그를 통한 요청 처리의 호환성을 위해 존재한다. Web MDN 에는 Simple Request 가 트리거되는 조건들이 상세하게 명시되어 있다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests

 

 

3. Spring boot 의 Preflight Request 처리 과정, 간단하게 살펴보기!

번외로, Spring 웹 프레임워크에서는 이러한 Preflight Request 를 어떻게 처리하는지 대략적으로 확인해보자.

 

먼저, DispatcherServlet 은 요청을 처리할 Handler 를 찾기 위해 아래와 같이 정의된 getHandler 를 호출한다. 그리고 mapping.getHandler(request); 에 의해 AbstractHandlerMapping 클래스에 정의된 getHandler 메서드가 불리게 된다.

 

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

 

 

이때, AbstractHandlerMapping 클래스에 정의된 getHandler 의 구현 내용 중 일부를 살펴보면 아래와 같이 서버 내에 CORS 설정이 되어있는지 또는해당 요청이 PreflightRequest 인지 검증하고있는 것을 확인해볼 수 있다.

 

 

	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		CorsConfiguration config = getCorsConfiguration(handler, request);
		if (getCorsConfigurationSource() != null) {
			CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
			config = (globalConfig != null ? globalConfig.combine(config) : config);
		}
		if (config != null) {
			config.validateAllowCredentials();
		}
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

 

위 검증을 통과하면 getCorsHandlerExecutionChain 메서드를 호출하여 요청이 PreFlightRequest 인 경우에는 PreFlightHandler 가 동작하며, 서버 내 CORS 설정이 되어있는 경우(이때는 PreFlightRequest 가 아닌 본 요청일 것이다.) CorsIntercepter 를 HandlerExecutionChain 에 추가해준다.

 

	protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
			HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

		if (CorsUtils.isPreFlightRequest(request)) {
			HandlerInterceptor[] interceptors = chain.getInterceptors();
			return new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
		}
		else {
			chain.addInterceptor(0, new CorsInterceptor(config));
			return chain;
		}
	}

 

분량 상 코드는 여기까지 살펴보겠지만, PreFlightHandler 가 동작하든 CorsIntercepter 가 동작하든 공통적으로 서버에서  허용하지 않은 (origin, http method 등에 대해) 요청이 올 경우 403(FORBIDDEN) 에러 코드를 응답하며, 허용된 요청이 올 경우에는 응답 헤더에 허용 origin, http method 등을 할당해주어 응답해준다.

 

 

4. SOP 의 한계

SOP 의 동작 매커니즘을 보면 CSRF 공격에 상당히 안전해보인다. 하지만, 아쉽게도 SOP 는 완벽하지 않다. 일단, 웹 브라우저에서의 모든 요청에 SOP 가 적용되는 것이 아니다. 예를 들면, 이미지 태그(<img src="">), 링크 태그(<link rel="" href="">) 등을 활용한 요청에는 SOP 가 적용되지 않는다. 이외에도 Web MDN 을 보면 SOP 가 적용되지 않는 요청들을 확인할 수 있다. 예로 든 태그 요청들의 경우 GET 요청인데, 이러한 사실로 하여금 서버 상 GET 요청 구현 시 리소스를 변경시키지 않도록 해야 함을 더욱 느끼게 한다.

 

아울러, 앞서 언급했었던 Simple Request 도 있다. Simple Request 는 서버의 CORS 정책과 상관없이 웹 브라우저와 서버간 요청/응답이 정상적으로 처리되기에, CSRF 공격에 노출될 위험이 크다. 특히, <form action="POST"> 같은 요청의 경우 서버의 리소스를 변경할 여지가 커 CSRF 공격에 더욱 취약하다.

 

또한, document 나 script 에 요청 헤더인 Origin 이나 Referer 을 조작하는 악성 코드를 심어 놓으면 CSRF 공격을 할 수 있지 않을까 생각할 수도 있다. 다만, Origin 이나 Referer 헤더를 설정하는 것은  브라우저의 통제 하에 있기에 사용자나 임의의 코드로 조작할 수 없다. 하지만, 웹 브라우저의 플러그인(악의적으로 개발된)을 통해 해당 헤더를 조작하는 경우의 수도 존재한다.

 

이처럼 예상치 못한 변수를 대비하기 위한 방법 중 하나로, 서버 측에서 클라이언트에 CSRF 방지 토큰(해시 값)을 응답하는 방법이 있다. 예를 들어, 서버는 악의적이지 않다고 판단되는 사용자에 대해 CSRF 방지 토큰을 응답하고, 서버는 특정 요청을 처리하기에 앞서 클라이언트에서 전송한 CSRF 방지 토큰을 검증해주는 것이다. 이를 통해, 악의적인(사용자가 의도치 않은) 요청(CSRF)과 정상적인(사용자가 의도한) API 요청을 구분할 수 있게 된다.

 

다만, 이로 인해 서버는 보다 stateful 해지고 클라이언트는 해당 CSRF 방지 토큰 관리 부담을 가지게 된다. 아울러, CSRF 방지 토큰 같은 경우에는 보안 상 만료 시간을 무한정으로 두기 보다도 (탈취, 유출 등 대비) 일정 만료 시간을 두는데, 사용자가 서비스에서 오래 걸리는 작업을 처리하고있을 때 이 토큰의 만료 기간이 지나면 중간에 서버로의 요청이 실패하여 UX 를 악화시킬 수도 있다. 이때, 토큰에 대한 refresh 전략에 따라 다시 경우의 수가 많아질 것이다.

 

여기서 하고싶은 말은 CSRF 방지 토큰이 좋다 나쁘다를 따지는 것이 아니라 이러한 장점과 단점을 명확히 이해하고 본인의 서비스 도메인과 시스템에 알맞게 적용하는 것이 중요하다는 것이다. 참고로, 이외에도 CSRF 공격을 예방하기 위한 많은 방법들이 있는데, 이번 글에서는 쿠키의 SameSite 속성에 초점을 맞추고자 한다.

 

 

5. 쿠키의 SameSite 속성에 대해 알아보자!

5-1. SameSite 란?

쿠키는 세션 관리, 사용자 트래킹 등 웹에서 다양한 용도로 사용되는 기술이다. 하지만, 이러한 쿠키는 사용자 PC 에 저장되며 웹 브라우저에서 쿠키의 출처(쿠키의 Domain 속성과 Path 속성으로 정의)와 동일한 서버에 요청 시 자동으로 전송되게 된다. 하지만 이러한 매커니즘은 CSRF 공격에 노출되기 쉽다.

 

이때, SameSIte 는 쿠키에 부여되는 속성 값으로서 None 과 Lax 그리고 Strict 가 있으며, 이러한 각각의 속성별로 cross site context 에서 쿠키 전송 유무를 제어할 수 있다. 이러한 특성으로 쿠키를 기반으로 하는 CSRF 공격에 대비하여 SOP 의 한계를 보완할 수 있다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value

 

 

5-2. Same Site vs Cross Site

SOP 를 다룰 때 Same Origin 과 Cross Origin 으로 구분했었던 것과 달리, 여기서는 Same Site 와 Cross Site 으로 구분한다. 여기서 Site 란 public suffix(com, co.kr, org, net, github.io 등) 기준 한 단계 하위 도메인까지만을 나타내는 것으로 Origin 과는 다소 차이가 있다.

 

Same Site 란 사용자의 현재 브라우저 주소 표시창에 있는 Site 정보와 HTTP 목적지의 Site 가 동일한 경우를 의미한다. 이때, Same Site 일 때 주고받는 쿠키를 first-party cookie (자사 쿠키) 라고 하며, Corss Site 일 때 주고받는 쿠키를 third-party cookie (타사 쿠키) 라고 한다.

 

 

참고로, 과거에는 scheme 은 은 Site 에 포함되지 않았기에, http 프로토콜의 Site 에서 https 프로토콜의 Site 로 쿠키를 전송할 수 있었으나, 보안 상 scheme 도 Site 에 포함시키는 브라우저가 상당수 있다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie

 

 

6. SameSite 속성별 필요성

6-1. SameSite = None

SameSite 의 None 속성은 SameSIte 속성 중 가장 개방적인 속성으로, Same Site 건 Cross Site 건 모든 문맥에서 쿠키가 전송 가능하게 된다. 이는 한 때 많은 브라우저들의 기본 속성값이었으나, 현재는 Lax 로 변경되는 추세이며, None 으로 설정 시 쿠키의 Secure 속성 설정을 강력하게 권고하고 있는 추세이다. 

 

참고로, 크롬 브라우저의 경우에는 SameSite 의 기본 속성이 Lax 이며 서버에서 None 으로 설정하는 경우에는 Secure 속성을 해주어야 설정할 수 있는데, Secure 속성은 https 로 통신할 때에만 쿠키를 주고받을 수 있는 것으로 사실상 https 를 강제하는 것과 다름없다.

 

None 속성은 모든 문맥에서 쿠키가 전송된다고 했는데, 그럼 언제 사용되는걸까? 예를 들면, 아래와 같은 시나리오가 존재한다.

 

 

티스토리와 유튜브는 명백히 Cross Site 이다. 따라서, 사용자가 유튜브로부터 받은 쿠키 데이터를 티스토리의 Site 에서 유튜브의 Site 로 전송하려면 쿠키의 None 속성이 필요하다. 티스토리에 임베딩된 유튜브 영상을 통해 유튜브 서버에 쿠키를 요하는 요청을 보내려면 유튜브 서버 측은 클라이언트에 쿠키를 응답해줄 때 SameSite 속성을 None 으로 지정해야하는 것이다. 하지만 이러한 쿠키는 CSRF 공격에 가장 취약하므로 신중하게 사용되어야 한다.

 

6-2. SameSite = Strict

반면에, Strict 속성은 무조건 Same Site 인 경우에만 쿠키를 전송할 수 있게 하는 것이다. SameSite 속성 중 가장 보수적인 속성이다. CSRF 공격에 가장 안전한 속성으로 서버 리소스를 변경시키는 등 파급력이 큰 행위에 쿠키로 하여금 권한을 부여할 때 사용되어야 한다.

 

예를 들면, 아래와 같이 Strict 를 활용하여 CSRF 공격에 방지하는 시나리오가 있다.

 

 

앞서, form 태그 요청은 Simple Request 로서 SOP 가 적용은 되지만 본 요청이 서버에 전송된다고 했었는데, 만일 사용자의 권한 정보를 지니는 쿠키가 SameSite 속성이 Strict 로 지정되었더라면 CSRF 공격을 당해도 쿠키가 서버로 전송되지 않아 서버 리소스를 변경하는 작업을 방지할 수 있는 효과가 있다.

 

6-3. SameSite = Lax

마지막으로 Lax 속성은 기본적으로 Strict 와 동일하나, 특정 경우에는 Cross Site 인 경우에도 쿠키를 전송할 수 있다. 여기서 특정 경우란 <a> 태그의 href 를 통한 요청이나, (top-level-navigation 일 때) document.location 을 통한 요청 등이 있다.

 

예를 들어, 현재 document 의 출처가 https://ikjo.com 이라면,

 

<Img src="https://hello.com/1.png"/> 의 요청은 쿠키가 전송되지 않는다. 반면, <a href="https://hello.com"></a> 와 document.localtion.href = "https://hello.com" 의 요청은 쿠키가 전송된다.

 

또한, <iframe id="ikjo"></iframe> 태그가 있을 때, script 를 통한 document.getElementById("ikjo").contentDocument.location.href = "https://hello.com" 요청은 쿠키가 전송되지 않는다. 왜냐하면 앞서 언급했듯이, top-lelvel-navigation 기반의 document.location 일 때만 Cross Site 를 허용하기 때문이다. 이때, contentDocument 는 iframe 태그에 의해 생성된 별도의 document 객체를 반환한다.

 

Lax 속성은 이러한 예외를 두었는데, 이러한 예외에 대한 예시 시나리오는 다음과 같다.

 

 

어떤 사용자가 쿠팡을 이용하다 쿠팡 서버로부터 받은 쿠키가 있는데, 티스토리 블로그 글들을 구경하다가 무심코 글에 임베딩된 쿠팡 광고를 누르는 경우가 있는데, 이러한 광고 배너는 주로 링크를 통해 접속하게 된다. 이때, 쿠팡 입장에서는 티스토리라는 Site 에서 쿠팡 이라는 Site 로 쿠키를 전송하지 못하게 되면 사용자를 트래킹할 수 없다. 이러한 경우 SameSite 속성을 Lax 로 설정해볼 수 있다.

 

 

7. 오늘날 SameSite 사용에 대한 고찰

다시 예전 프로젝트 경험으로 되돌아오면 당시 우리 서버 측에서 별다른 SameSite 의 속성을 부여하지 않았기에 크롬 브라우저 기반에서 테스트하던 프론트 엔드 측에서는 SameSite 속성이 Lax 인 쿠키를 받게됐던 것이다. 이때, 프론트 엔드 팀원들이 작업했었던 환경은 로컬 기반의 node.js 였기에, localhost 라는 도메인을 사용하고있었고 target server 는 AWS 에 배포되어 일반적인 도메인(ex. https://ikjo.com)을 띄고있었기에 Cross SIte 문맥이었고, 클라이언트 측의 일반적인 axios 호출의 경우 모두 쿠키가 서버로 전송되지 않게 되는 것이다.

 

이러한 SameSite 를 통해 얻을 수 있는 점을 다시 정리해보면 일단 CSRF 를 예방할 수 있다는 것이다. 아울러, 서버 측에서 클라이언트로 쿠키를 응답함에 있어 쿠키 전송을 Site 및 상황별로 제어할 수 있기에 쿠키 정보가 불필요하게 thrid-party 로 흘러들어가는 것을 방지할 수도 있다. 그리고 간혹 IT 지식에 해박한 일반인들도 본인의 웹 브라우저가 관리하는 쿠키들의 SameSite 속성을 토대로 자신의 쿠키가 어떻게 활용되고있는지를 아는 경우도 있는 것 같다.

 

이처럼 서버 개발자 입장에서는 쿠키를 클라이언트에 응답함에 있어 SameSIte 속성을 명확히 알고 사용하는 것이 중요하다고 생각한다. 우선, 해당 쿠키가 자사(first-party) 쿠키로 사용할지 (third-party) 쿠키로 사용할지를 생각해봐야 하며 SameSite 의 기본 속성에 의존해서는 안된다. 왜냐하면 SameSite 의 기본 속성은 브라우저별로 다르기 때문이다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie

 

위 표를 보면, 모든 브라우저에서 SameSite 속성을 지원하긴 하나, 이에 대한 기본 속성을 Lax 로 하냐 안하냐는 브라우저별로 상이하다. 즉, 개발자는 쿠키를 사용 용도에 따라 분리하고 명확하게 SameSite 의 속성을 명시해줄 필요가 있다고 생각한다. 예를 들어, Lax 속성의 경우 a 태그를 통해서 요청 시 쿠키 전송이 가능하기에 해당 쿠키를 수반한 요청의 경우 서버 리소스를 변경하면 CSRF 공격에 위험하다. 피치 못하게 SameStie 속성을 None 으로 해야한다면 앞서 잠시 언급했었던 CSRF 방지 토큰을 사용하는 방법도 있다.

 

또한, SameSite 속성을 Strict 으로 한다고 100% CSRF 에 안전한 것은 아니다. 대표적으로 client side redirect 를 이용하거나 sibling domain 을 이용하여 Strict 속성을 우회하는 방법이 있다. (자세한 내용은 Web Security Academy 를 참고) 하지만, 모든 상황에서 적용되는 것은 아니고 일부 상황에 대해 경우의 수가 존재하는 것이다.

 

개인적으로 공격에 대한 모든 경우의 수를 차단하고자 하면 할수록 성능이 저하될 요소들이 많아지는 것을 느낄 수 있었다. SOP 의 한계를 다룰 때에도 잠시 언급했듯이 결국 개발자는 어떤 기술을 도입함에 있어 도메인과 시스템 등의 상황을 제대로 이해하고 장점과 단점을 균형있게 가져가는 것이 중요하다고 생각한다.

 

 

참고자료

  • https://stackoverflow.com/questions/24680302/csrf-protection-with-cors-origin-header-vs-csrf-token
  • https://stackoverflow.com/questions/21058183/whats-to-stop-malicious-code-from-spoofing-the-origin-header-to-exploit-cors
  • https://en.wikipedia.org/wiki/Cross-site_request_forgery 
  • https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
  • https://www.youtube.com/watch?v=Q3YuKipzPbs
  • https://www.youtube.com/watch?v=6QV_JpabO7g