쿠키에 대하여

천영석·2021년 9월 2일
2

갑자기 쿠키?!

이 글을 작성하는 이유는 쿠키에 대해 이것저것 실험해보면서 알게된 것들을 정리하기 위함이다.

쿠키는 브라우저에 저장되는 작은 크기(최대 4KB)의 문자열이다. 이들은 HTTP 요청 시 자동으로 Headers에 전송된다는 특징이 있다. 이 특징으로 인해 CSRF에 취약하게 된다. (XSS 보안 문제도 있지만 여기에서는 언급하지 않겠다.)

CSRF

CSRF란 간단하게 이미 로그인되어 있는 사용자의 권한을 사용해 사용자의 비밀번호를 변경하거나 결제 요청을 하는 등 악의적인 요청을 하는 것이다.
만약 비밀번호를 변경한다고 생각해보자. https://example.com/changepassword?newPassword=123 이라는 주소로 요청하면 사용자의 쿠키를 확인하고, 비밀번호를 123으로 변경해준다고 가정하자. 서버 입장에서 볼 때, 사용자의 인증 정보가 담긴 쿠키가 넘어왔고, 비밀번호를 123으로 변경해달라는 주소를 받았기 때문에 정상적으로 비밀번호가 변경될 것이다. 만약 해당 요청이 GET 요청으로 이루어진다면 더 큰일이다. img, iframe, form, svg, a tag 등 서버에 요청을 보낼 수 있는 모든 태그로부터 공격이 가능해진다.
<img src="https://example.com/changepassword?newPassword=123" />로 보내면 끝이다.

다시 예시를 들어보자면 이미 example.com 사이트에 로그인이 되어 있는 사용자는 쿠키가 발급되었을 것이고, 브라우저에 쿠키가 저장되어 있을 것이다. 그 뒤 example.com에 대한 보안을 강화하세요! 라는 메일을 받게 되고, 그 링크를 클릭하면 해커가 만든 좋지 않은 사이트로 연결될 것이다. 이 사실을 모르는 사용자는 해당 링크를 클릭할 것이고, 해당 링크에는 <img src="https://example.com/changepassword?newPassword=123" /> 가 담겨있다.
사용자는 이미 example.com 사이트의 쿠키를 가지고 있기 때문에 아무것도 모르는 브라우저는 어! 내가 생성된 도메인이다!! 따라가야지~ 하고 요청에 같이 담겨서 서버로 들어갈 것이다. 또한 아무것도 모르는 서버는 사용자에게 패스워드 변경해달라는 요청이 들어왔네? 쿠키를 보니 이 유저군!! 비밀번호를 변경해주자 라고 판단해 패스워드를 변경한다. 이제 해커는 해당 사용자의 비밀번호로 로그인을 할 수 있게 되었다.

여기까지가 내가 이해한 CSRF의 대략적인 요약이다. 사실 지금까지 리액트로 프로젝트를 해왔기도 하고 아직 SSR을 적용하지 않았으며, api를 통한 요청이 대부분이기 때문에 주소를 통해 공격하는 CSRF가 그렇게 와닿지는 않는다. 무엇보다도 RESTful API를 따르고 있기 때문에 GET 요청에 패스워드 변경같은 수정은 하지 않는다. 하지만 알아두면 좋을 것 같아서 더 공부해봤다.

CSRF 방어! - SameSite

CSRF를 막기 위해서는 SameSite 옵션 하나면 끝난다. SameSite는 추가된지 그렇게 오래되지 않은 것 같다. 이전에는 referer을 검증했었다.

SameSite는 같은 도메인에서의 요청이 아닌 경우에는 쿠키를 전송하지 않는 옵션이다. 서드파티 쿠키를 심으려는 목적이 아니라면 항상 SameSite 옵션을 적용하는 것이 좋아보인다.

SameSite는 Strict와 Lax 옵션이 있는데, Strict는 같은 도메인이 아닌 경우에 어떤 경우라도 쿠키를 전송하지 않는 것이고, Lax는 a태그를 통한 링크 이동과 같은 안전한 GET 요청인 경우에만 쿠키를 전송한다는 옵션이다. img나 iframe, script 같은 GET 요청에는 전송되지 않는다.

Lax 옵션이 언제 필요해서 존재하는 것인지는 정확히 모르겠다.(더 알아보고 추가할 예정)

같은 도메인이 아닌 경우 쿠키를 전송하지 않는다고 했는데, 요청을 보내는 주소창을 확인한다. 쿠키의 생성 도메인이 .example.com일 때, https://example.com/abc으로 요청을 한다면 도메인이 같기 때문에 전송된다. 이 경우에 만약 https://m.example.com으로 요청을 한다고 해도 전송된다. 왜냐하면 서브 도메인도 같은 도메인으로 취급하기 때문이다. 이는 퍼스트파티 쿠키의 특징이다.

하지만 SameSite가 적용되어 있다면 다른 도메인 https://differ.com에서 https://example.com/abc로 이동시키고, 해당 도메인(example.com)에 대한 쿠키가 있다고 하더라도 쿠키가 전송되지 않는다.(strict일 경우)
해당 네트워크 탭을 열어보면
위와 같이 cross-site라고 표시되는 것을 볼 수 있을 것이다. 즉, 다른 도메인으로부터 시작된 요청이기 때문에 쿠키가 전송되지 않는다.

이렇게 cross-site라고 표시되지만 안전한 GET 요청인 a 태그를 통한 링크 이동의 경우에만 쿠키를 전송하는 것이 SameSite=Lax이다. (form의 GET도 된다고 하는데 실험해보지는 않았다.)

주소 창에 직접 주소를 검색해서 접속하지 않는 한 Strict의 쿠키를 전송할 수가 없는 것이다.
아.. 글을 작성하다보니 Lax 옵션이 왜 있는지 알게 되었다. 만약 구글에서 네이버를 a태그를 통해 접속한다면 cross-site이지만 정상적으로 접근한 것이기 때문에 쿠키는 전송되어야 한다. 만약 Strict만 있다면 구글에서 네이버를 검색해서 들어가도 네이버의 쿠키가 전송되지 않기 때문에 문제가 될 수 있을 것 같다.

아, 추가적으로 naver.com에서 mail.naver.com으로 a 태그를 통한 링크 이동은 cross-site가 아니라 same-site로 인식된다. 서브도메인이기 때문에 같은 사이트라고 인식되는 것 같다.

내 생각에 SameSite만 적용한다면 XSS에 노출이 되어 있지 않은 이상 CSRF 공격을 당할 일은 없을 것 같다.

서드파티 쿠키는 어떻게 생성되는 것일까?

궁금한 것이 또 있었는데, 어떻게 서드파티 쿠키가 생성되는지에 관한 것이었다. 내가 즐겨보는 크루의 블로그가 있다. 티스토리인데, 이 블로그에는 카카오, 페이스북, 트위터 등 여러가지의 서드파티 쿠키가 있는 것을 볼 수 있다.

하지만 위의 그림은 zereight.tistory.com의 안에 있는 쿠키이고, 아래 그림은 twitter, facebook으로 아예 다른 위치에 쿠키가 저장되는 것을 볼 수 있다.

어떨 때 현재 도메인에 서드파티 쿠키가 생성되고, 어쩔 때 다른 위치에 서드파티 쿠키가 생성되는 것인지가 궁금했다. 이것도 실험을 하다가 알게 되었는데, html 파일 내에 iframe이 있다면 아래 그림처럼 새로운 위치에 쿠키를 생성한다. 이때, 로컬스토리지와 세션스토리지도 해당 도메인으로 따로 생성된다. 생각해보면 당연한 것 같다. iframe은 html 안에 중첩해서 html을 만드는 것이기 때문이다. html이 새로 생겼으니 새로운 쿠키와 스토리지를 가지는 것도 당연하다.

그러고 위의 그림처럼 현재 도메인 내에서 서드파티 쿠키가 생성되는 것은 script, img, svg와 같은 태그로 해당 도메인으로 요청을 할 때 생성된다. 이때, 위와 같이 어플리케이션 탭에서 쿠키가 확인되어도 실제 요청에 쿠키가 전송되지 않는 경우도 있다. SameSite 옵션이 활성화되어 있다면 전송되지 않기 때문이다.

이렇게해서 내가 궁금했던 것들을 해결할 수 있었다. 시간은 오래 걸렸지만 재미있고 유익한 시간이었다.

api요청 시 쿠키는?

기본적으로 fetch나 XHR은 cross-origin에 대해 쿠키를 전송하지 않는다. 하지만 프론트엔드 서버와 백엔드 서버가 다른 경우가 있다.(내가 지금까지 한 프로젝트는 다 달랐다.) 이때 어떻게 쿠키를 전송할 수 있을까?

서버에서 CORS 옵션에 프론트서버 주소를 적고(*은 안됨!), credentials를 true로 변경해주어야 한다. 또한, 프론트 서버에서도 credentials 옵션을 설정해야 한다. fetch의 경우에는 credentials: 'include'를, xhr이나 axios의 경우에는 withCredentials: true로 설정하면 된다.

그러면 이제 cross-origin일 때에도 쿠키를 받거나 보낼 수 있게 된다.

cross-origin일 때 credentials와 같은 설정을 모두 끝내고 api를 통해 쿠키를 받게 되면 도메인이 어디로 설정될지 궁금하다. 아무래도 백엔드 서버의 도메인으로 설정될 것 같다. 이것도 실험을 해봐야겠다.

요약

  • 쿠키는 CSRF에 노출이 되어 있다.
  • 이는 SameSite 옵션으로 해결할 수 있다.
  • 서드파티 쿠키는 해당 도메인으로 요청하는 태그(script, img, svg, iframe 등)가 html 내에 존재할 때 생성된다.
profile
느려도 꾸준히 발전하려고 노력하는 사람입니다.

0개의 댓글