단일 서비스의 사용자 인증 기능을 위해 동작하던 "사용자 인증 애플리케이션"에서
다수 서비스의 사용자 인증 기능을 제공하는 서버로 동작해야함과 동시에
해당 서버를 통해 인증된 사용자는 사내 다른 서비스에 접속시에도 로그인 상태를 공유해야했기 때문에 SSO 기능까지 가능하도록 구현해야했습니다.
어떤 방식으로 구현할 지 고민한 내용 중 JWT 토큰을 저장할 위치와 관련하여 "쿠키" 에 대해 알아본 내용들을 정리합니다.
SSO 기능을 제공하려면 크게 두가지 요구사항이 있었습니다.
1) 로그인 후, 새 탭에서 접속했을 때에도 로그인이 되어야 한다.
2) 서브 도메인이 같으면 인증 토큰이 공유되어야 한다. (단, 다른 서브 도메인에 접속 시에는 쿠키가 전달되지 않는다.)
기존 "사용자 인증 애플리케이션"은 사용자 인증 과정을 거치고 나면 JWT를 발급해주고 있었습니다. body에 토큰을 담아 응답하면 Frontend에서 이를 세션 스토리지에 저장하고 있었어요.
잠시 세션 스토리지와 로컬 스토리지, 그리고 쿠키에 대해 알아보겠습니다.
Session Storage
특징
- 간단한 key-value를 저장하는 스토리지입니다.
- 말그대로 브라우저 세션 동안만 데이터가 유지됩니다.
- 탭 간에도 공유되지 않기 때문에 같은 브라우저라도 다른 탭을 띄우면 데이터는 공유되지 않습니다.
- 브라우저 또는 탭을 닫으면 데이터가 삭제됩니다.
활용
세션 스토리지는 브라우저 탭 내로 제한되기 때문에 잘 활용되지 않습니다.
잠시 필요한 정보 (일회성 로그인) 같은 데이터는 저장할 수도 있습니다.
Local Storage
특징
- 간단한 key-value를 저장하는 스토리지입니다.
- 세션 스토리지와 유사하지만, 오리진(domain/port/protocol)이 같으면 (동일 출처 내의) 모든 탭에서 데이터가 공유됩니다.
- 호스트 : https://www.example.com의 로컬 스토리지 데이터는 https://api.example.com과 공유되지 않습니다.
- 프로토콜 : http://example.com에서 저장한 로컬 스토리지 데이터는 https://example.com과 공유되지 않습니다.
- 포트 번호 : https://example.com:8080에서 저장한 로컬 스토리지 데이터는 https://example.com:3000과 공유되지 않습니다.
- 브라우저를 종료하거나 OS를 재시작해도 데이터가 삭제되지 않습니다.
활용
지속적으로 필요한 데이터 를 저장하는데 활용됩니다.
세션 스토리지와 로컬 스토리지보다 먼저 나왔던 기능이 쿠키 입니다.
Cookie
특징
- HTTP Cookie는 세션 스토리지/쿠키 스토리지와 다르게 데이터를 저장할 때 만료 기한을 정할 수있습니다.
- 웹사이트에 접근하는 디바이스에 Cookie가 저장되며, 여러 개의 Cookie가 저장될 수 있으나 4kb라는 용량 제한이 있습니다.
- Cookie는 브라우저 단위로 생성됩니다. 즉 Chrome, Explorer, Edge, Safari, FireFox, Opera 등 서로 다른 브라우저에서 생성된 Cookie는 서로 다른 것으로 인식됩니다.
- 요청을 할 때마다 서버로 저장된 Cookie도 함께 전달되기 때문에 그중 필요하지 않은 데이터들이 있다면 용량을 불필요하게 낭비하게 됩니다.
서버로 전송되지 않아도 되는 데이터들은 로컬/세션 스토리지에 저장하는 것입니다.
활용
HTTP 요청 자체만 보낼 때에는 서버가 누구인지 알 수 없습니다.
하지만 Cookie에 내가 누구인지 담아서 서버에 요청을 보낼 때 함께 전달하면 서버는 Cookie를 읽어서 요청자가 누구인지 파악합니다.
이처럼 Cookie는 웹 서버가 사용자의 기기에 'stateful'한 정보를 저장할 수 있도록 하기 때문에
온라인 쇼핑몰에서 고객의 장바구니에 아이템을 추가한 상태 정보를 저장하거나 '일주일간 다시보지 않기'와 같은 기능에 사용되는 등 "사용자의 브라우저 활동을 트래킹"하는데 활용됩니다.
그리고 제가 Cookie를 사용하고자 하는 목적이기도 한,
웹서버에서 사용자가 로그인했는지, 그들이 어떤 계정으로 로그인했는지를 "인증"하는데에도 사용될 수 있습니다.
사용자가 인증을 거치고 나면 Cookie에 정보를 저장함으로써 사용자가 민감한 정보를 포함한 페이지 각각에 접근할 때 마다 로그인을 통해 스스로를 인증해야 하는 불편함을 없앨 수 있습니다.
최종적으로, 하위 도메인 간 값이 공유되어야 하기 때문에, 이를 지원하는 쿠키를 선택했습니다. (쿠키는 하위 도메인이 동일하면 공유가 가능합니다.)
또한 새 탭으로 로그인했을 때 쿠키 스토리지 내 값은 사라지지 않는다는 특징이 있었습니다.
그래서 쿠키에 JWT 토큰을 저장하는 방식으로 시도해보기로 하였습니다.
다만 쿠키에 값을 저장하면 서버에 요청을 보낼 때 자동으로 값이 전송되기 때문에 보안 공격에 취약합니다.
또한 쿠키에 값을 저장할 때 얼마나 값을 유지할지도 설정할 수 있는데요 이 기간도 길게 설정한다면 보안적으로는 더 취약하겠죠.
그래서 쿠키를 적용하면서 최대한 보안에 취약하지 않는 방안이 있을지 찾아보았습니다.
그 과정에서 알게된 쿠키의 옵션들은 아래와 같습니다.
Cookie의 옵션 종류
Cookie에 값을 저장할 때는 다양한 종류의 옵션을 함께 지정할 수 있는데, 이 옵션들을 통해 브라우저에서 정보가 저장되고 공유될 수 있는 상태를 제한할 수 있습니다.
Max-Age
Cookie가 브라우저에 유지되는 최대 설정 옵션 (단위 : second)
- -1로 세팅 시 브라우저가 종료될 때까지 유지되는 Cookie가 생성된다. (일명 '세션 쿠키')
Expires
- 쿠키가 만료되는 날짜를 설정하는 옵션
- 'Session' 이라는 값으로 세팅 시 브라우저 종료될 때까지 유지되는 Cookie가 생성된다. (일명 '세션 쿠키')
Session cookie 와 Persistent cookie
1. Session Cookie
인메모리 쿠키라고도 알려져있는 세션 쿠키는 메모리에 저장됩니다.
사용자가 웹사이트를 돌아다닐 동안 즉, 브라우저 세션 동안만 일시적으로 존재합니다.
결국 웹 브라우저를 닫으면 자동으로 삭제되는 것이 가장 큰 특징입니다.
그렇기 때문에 아래에 나올 Persistent cookie (지속성이 있는 쿠키) 보다 보안 측면에서 유리합니다.
반면 Persistent cookie는 말 그대로, 브라우저에 오랜 기간 저장되기 때문에 그만큼 공격에 노출될 가능성이 높습니다.
세션 쿠키로 세팅하기 위해서는 쿠키의 유효 시간을 지정하는 쿠키의 옵션인 Expires 옵션을 'Session'으로 세팅하거나 Max-Age 옵션을 -1로 사용하면 됩니다.
사용자가 브라우저를 닫을 때 무조건 쿠키에 저장한 정보가 초기화되어야 한다면 세션 쿠키를 사용하면 된다.
2. Persistent Cookie
지속성이 있는 쿠키는 특정 일자나 기간에 만료되는 쿠키입니다.
Persistent cookie는 디스크에 저장되기 때문에, 브라우저를 닫거나 컴퓨터를 재시작하더라도 남아있게 된다.
이러한 기능 덕분에,
사용자가 브라우저를 닫고 다시 재접속했을 때에도 이전에 등록했던 정보들을 저장하고 있어 사용자가 재접속했을 때 로그인을 유지시켜주는데 쓰이기도 합니다.
앞서 말한 대로, Persistent cookie는 Session cookie보다 공격에 노출될 가능성이 높기 때문에 보안에 도움이 되는 여러 옵션들과 함께 생성하길 권장하고 있습니다.
Secure
해당 옵션이 설정되면, 쿠키가 암호화된 연결 간에만 전달될 수 있도록 하고 암호화되지 않은 연결 간에는 전달되지 못하도록 합니다. HTTPS가 대표적입니다.
이를 통해, 네트워크 도청 (스니핑/스누핑 공격) 으로 인해 쿠키가 도난당할 가능성을 줄여줍니다.
Http-only
해당 옵션이 설정되면, Javascript와 같은 클라이언트 사이드에서 접근할 수 없게 됩니다.
이러한 제한은 XSS 공격으로부터 막아주는 효과가 있습니다.
XSS 공격 (Cross Site Scripting, XSS) 이란?
공격자가 웹 페이지에 악의적인 스크립트를 삽입하여, 사용자의 브라우저에서 실행되게 만드는 공격입니다.
이를 통해 공격자는 사용자의 세션 쿠키를 탈취하거나, 사용자를 가장하여 악의적인 행위를 수행할 수 있습니다.
기본적으로 Cookie는 Javascript를 통해 조회 가능한데, 쿠키를 HttpOnly 설정하면 이를 막을 수 있는 효과가 있습니다.
하지만 HttpOnly 옵션을 설정하면 서버에서만 쿠키를 읽을 수 있기 때문에 한번 이상의 서버로의 API 호출이 불가피합니다.
즉 클라이언트 측에서 쿠키 값을 확인하거나 수정하는 작업이 불가능합니다. 이러한 단점을 감수하더라도 보안을 위해 HttpOnly 옵션을 사용하는 것이 안전합니다.
Same-site
Same-site 옵션은 쿠키를 전달할 수 있는 정도를 제한하는 옵션입니다.
상태는 총 세가지로 설정할 수 있습니다. Strict / Lax / None
- Strict
브라우저는 하위 도메인이 같은 도메인으로만 쿠키를 전달할 수 있도록 제한합니다. 즉 퍼스트(first) 쿠키만이 전달됩니다.
blog.naver.com 과 map.naver.com 은 하위도메인이 .naver.com 으로 같기 때문에 쿠키를 공유할 수 있습니다.
이는 CSRF 공격에 방지하는데 도움이 됩니다.
CSRF 공격이란?
CSRF 공격은 사용자가 로그인한 상태에서 공격자가 준비한 악의적인 요청을 서버에 전송하게 만듭니다. 이 과정에서 사용자는 자신도 모르는 사이에 공격자가 의도한 행위를 실행하게 됩니다.
SameSite 쿠키 속성을 설정하면, 하위 도메인이 동일한 곳으로만 쿠키를 전달하도록 제한하여 쿠키가 의도치 않은 곳으로 전송되는 것을 막을 수 있습니다.
- Lax
브라우저는 하위 도메인이 같은 도메인으로 쿠키를 전달할 수 있을 뿐만 아니라
하위 도메인이 다른 도메인에도 쿠키를 전달할 수 있지만, 이때는 서버의 상태를 바꾸지 않을 것이라 기대하는 요청(ex. GET 메서드) 시에만 가능합니다.
또한 다른 출처의 요청인 third-party 쿠키 는 전달 불가합니다. 다만 예외적으로 Top Level Navigation (웹 페이지 이동) 의 경우 유저가 링크를 누르거나 리다이렉트를 통해 이동된 경우에는 third-party 쿠키가 전달됩니다.
ㄴ <img> 태그나 <iframe> 태그 내에서 페이지를 이동하는 경우에는 Top Level 이라 부를 수 없으므로 전송되지 않습니다.
- None
동일한 하위 도메인이든, 다른 도메인이든 상관없이 쿠키가 전달 됩니다.
단, None으로 하려면 무조건 쿠키의 Secure 옵션을 true로 설정해야 정상 전송됩니다.
ㄴ 만약 SameSite 속성을 None 으로 설정했음에도 Secure 속성이 false라면 쿠키가 전달되지 않습니다.
크롬 브라우저는 2020년 2월 배포된 80버전부터 same-site 옵션의 기본값을 Lax로 설정했습니다.
HSTS (HTTP Strict Transport Security)
보안에 취약하지 않게 하는 또다른 방법으로, HTTPS를 강제하는 기술이 있습니다.
HSTS는 브라우저와 웹 서버 간 통신 시에 무조건 HTTPS 통신만 할 수 있도록 강제하는 기술입니다.
이용자가 웹 서버에 처음 접속할 때, 응답 헤더에 Strict-Transaport-Security 헤더를 내려보내서
HSTS를 지원하는 브라우저라면 이를 해석하고 추후 접속 부터는 해당 도메인에 대해 HTTPS 로만 접속합니다.
만약 브라우저에서 HTTP 접속이 시도되면
HTTP 응답 코드 302를 이용해 페이지 이동(redirection)이 일어나면서 강제로 HTTPS로 프로토콜을 변경시킵니다.
HSTS 설정 시 지정할 수 있는 값이 3개 있습니다.
설정 | 값 | 설명 |
max-age | 31536000 (s) -> 1년 | 해당 시간동안 HSTS 응답을 받은 웹 사이트에 대해 HTTPS 접속을 강제함. |
includeSubDomains | true | HSTS가 적용될 도메인의 서브 도메인까지 적용되고 있음을 알려줌. |
preload | true | 브라우저의 Preload List에 도메인 추가할 것을 알려줌. |
응답 헤더에 설정된 모습 :
Strict-Transport-Security: max-age=31536000; includeSubDomains ; preload
네이버를 예로 봐보겠습니다.
www.naver.com 호출 시 응답 헤더 내에 Non-Authoritative-Reason: HSTS 라는 세팅과 함께 응답을 받고 (사진)
그 후 바로 https로 리다이렉트 됩니다. (초록색 Network)
HSTS Preload List
HSTS Preload List는 HSTS가 적용된 웹 사이트의 명단 정보에 대한 리스트 입니다.
본인의 서비스에 HTTPS 적용을 한 후 HSTS Preload List에 등록하면,
이용자가 서비스에 처음 접속하는 브라우저에서부터 HTTPS로 접속하게 할 수 있습니다.
다만 등록하기 위해 준수해야하는 가이드가 있고,
가입 후 SSL 보안인증서 만료 등과 같이 가이드를 지키지 못하는 이슈가 있을 때 사이트가 접속이 안될 수 있다는 점,
추후 해당 리스트에서 삭제하고자 할 때 진행이 오래 걸린다는 점 등에서
따로 HSTS Preload List에 등록하지는 않았습니다.
네이버의 경우도 최초에 접속 시 http에서 https로 리다이렉트되는 것을 보면 알 수 있듯, HSTS Preload List에 등록되지 않은 상태입니다.
참고
- https://en.wikipedia.org/wiki/HTTP_cookie
- https://yozm.wishket.com/magazine/detail/1862/
- https://www.zerocho.com/category/HTML&DOM/post/5918515b1ed39f00182d3048
끝.
'백엔드 개발하며 작성한 > 기억할 내용' 카테고리의 다른 글
예외(Exception) 처리 (0) | 2024.06.14 |
---|---|
Web Server와 Web Application Server(WAS) (0) | 2022.04.25 |