이전 포스트
[베어랜드 Auth 시스템] 1 - 쿠키와 세션 그리고 JWT
쿠키와 세션, 그리고 토큰 기반 인증 시스템 구현하기이 글은 최근 프로젝트를 진행하며 Auth 시스템 구현에 대해 고민하고 적용했던 과정을 쿠키와 세션부터 토큰 기반 인증까지 정리한 첫 번째
bearn-soo.tistory.com
Refresh Token에 JWT가 필요할까?
Refresh Token을 서버에서 관리할 때, JWT처럼 정보를 포함하는 토큰을 사용할 필요가 있을까요? JWT는 자체적으로 정보를 담고 있는 "상태가 있는" 특성을 가지고 있습니다. 그러나 우리는 이미 Refresh Token의 상태를 서버 데이터베이스에서 관리하고 있습니다.
베어랜드 예시로 설명하면
- JWT 형태의 발급증은 "이 발급증은 A 손님의 것이며, 3개월간 유효합니다"라는 정보가 발급증 자체에 쓰여 있는 것과 같습니다.
- 하지만 베어랜드는 이미 내부 시스템에 발급증 정보를 기록하고 있으므로, 발급증에 이러한 정보를 적을 필요가 없습니다.
- 오히려 정보가 새겨진 발급증은 누군가 훔쳐 읽을 경우 보안 위험이 있습니다.
따라서 Refresh Token은 JWT보다 의미를 알 수 없는 불투명한 토큰(Opaque Token)을 사용하는 것이 보안상 더 유리합니다.
Opaque Token으로 전환
Opaque Token은 의미를 알 수 없는 무작위 문자열로, 서버의 데이터베이스에서만 의미를 가집니다.
베어랜드 예시로 설명하면
- 베어랜드는 손님에게 "XYZ987"이라는 무작위 코드가 적힌 발급증을 제공합니다.
- 이 코드 자체는 아무런 의미가 없으며, 발급증만 보고는 누구의 것인지, 언제까지 유효한지 알 수 없습니다.
- 베어랜드는 내부 시스템에 "코드 XYZ987은 김현수 손님의 것으로 3개월간 유효함"이라고 기록해둡니다.
- 손님이 발급증을 제시하면, 베어랜드는 내부 시스템에서 코드를 조회하여 유효성을 확인합니다.
베어랜드의 Refresh 토큰 보안 취약점과 개선
토큰 탈취의 위험성
만약 누군가 방문객의 Refresh Token을 탈취한다면 어떻게 될까요?
베어랜드 예시로 설명하면
- 악의적인 누군가가 손님의 발급증을 몰래 복사했다고 가정해봅시다.
- 이 복사된 발급증으로 무제한으로 새로운 입장권(Access Token)을 발급받을 수 있게 됩니다.
- 이는 베어랜드의 보안 시스템에 심각한 취약점이 됩니다.
이러한 위험에 대응하기 위해 두 가지 주요 전략을 적용할 수 있습니다.
개선 - 토큰 로테이션(Token Rotation)
첫 번째 전략은 토큰 로테이션입니다.
베어랜드 예시로 설명하면
- 손님이 발급증을 사용하여 새 입장권을 받을 때마다, 베어랜드는 기존 발급증을 회수하고 새로운 발급증을 함께 제공합니다.
- 만약 누군가 발급증을 복사해갔더라도, 원래 손님이 한 번이라도 새 입장권을 받으면 복사본은 더 이상 사용할 수 없게 됩니다.
- 이는 "한 번 사용된 발급증은 무효화된다"는 원칙을 적용한 것입니다.
개선 - 토큰 블랙리스트(Token Blacklist)
두 번째 전략은 토큰 블랙리스트입니다.
베어랜드 예시로 설명하면
- 손님이 베어랜드를 떠날 때(로그아웃), 발급증을 반납하면 베어랜드는 해당 발급증 번호를 "사용 불가" 목록에 추가합니다.
- 이후 누군가 이 발급증 번호를 사용하려고 하면, 베어랜드는 "사용 불가" 목록을 확인하고 서비스를 거부합니다.
- 이는 "명시적으로 무효화된 발급증은 사용할 수 없다"는 원칙을 적용한 것입니다.
토큰들을 어디에 저장할까?
토큰을 어디에 저장하는지도 인증 시스템의 보안에 중요한 요소입니다.
Refresh Token: HTTP-Only 쿠키에 저장
Refresh Token은 보안이 중요하므로, JavaScript로 접근할 수 없는 HTTP-Only 쿠키에 저장합니다.
베어랜드 예시로 설명하면
- 발급증(Refresh Token)은 손님의 특별한 주머니(HTTP-Only 쿠키)에 안전하게 보관됩니다.
- 이 주머니는 손님도 직접 열어볼 수 없지만(JavaScript 접근 불가), 베어랜드 입구에 도착하면 자동으로 내용물이 베어랜드 직원에게 전달됩니다.
- 이렇게 하면 악의적인 스크립트가 손님의 발급증을 훔쳐보기 어렵습니다(XSS 공격 방지).
Access Token: 인메모리 방식으로 저장
Access Token은 어디에 저장하는 것이 좋을까요? localStorage나 sessionStorage는 JavaScript로 쉽게 접근할 수 있어 XSS 공격에 취약합니다. 따라서 인메모리 방식을 선택했습니다.
베어랜드 예시로 설명하면
- 입장권(Access Token)은 손님이 주머니나 가방이 아닌 머릿속에 기억하고 있는 것과 같습니다(JavaScript 변수).
- 베어랜드를 잠시 나갔다가 다시 들어오면(페이지 새로고침) 입장권을 기억하지 못합니다.
- 이럴 때는 발급증(Refresh Token)을 제시하여 새 입장권을 받아야 합니다.
이러한 방식은 보안성이 높지만, 사용자가 페이지를 새로고침할 때마다 새로운 Access Token을 발급받아야 하는 단점이 있습니다.
베어랜드의 전체 인증 흐름
지금까지 설명한 요소들을 종합하여 베어랜드의 인증 시스템 흐름을 정리해보겠습니다
- 로그인:
- 손님이 베어랜드에 처음 방문하여 신원을 확인합니다(로그인).
- 베어랜드는 입장권(Access Token)과 발급증(Refresh Token)을 제공합니다.
- 입장권은 손님이 기억하고, 발급증은 특별한 주머니에 보관됩니다.
- 시설 이용:
- 손님은 베어랜드 내 시설을 이용할 때마다 입장권을 제시합니다(API 요청).
- 입장권이 유효하면 서비스를 이용할 수 있습니다.
- 입장권 만료:
- 입장권은 짧은 시간(예: 15분)이 지나면 만료됩니다.
- 만료된 입장권으로는 시설을 이용할 수 없습니다.
- 손님은 발급증을 제시하여 새 입장권을 받습니다.
- 이때 베어랜드는 기존 발급증을 회수하고 새 발급증도 함께 제공합니다(토큰 로테이션)
- 퇴장(로그아웃):
- 손님이 베어랜드를 떠날 때 발급증을 반납합니다.
- 베어랜드는 이 발급증 번호를 "사용 불가" 목록에 추가합니다(토큰 블랙리스트)
- 입장권도 폐기됩니다(메모리에서 삭제)
베어랜드 최종 인증시스템
- 토큰 구성:
- Access Token: JWT 형식, 짧은 유효 기간(15분), 사용자 정보 포함
- Refresh Token: Opaque Token 형식, 긴 유효 기간(예: 30일), 무작위 문자열
- 보안 전략:
- 토큰 로테이션: 매 Refresh 시 새로운 토큰 쌍 발급
- 토큰 블랙리스트: 로그아웃된 토큰 관리
- 저장 방식:
- Access Token: 인메모리 저장
- Refresh Token: HTTP-Only 쿠키 저장
이 인증 시스템은 XSS나 CSRF 공격은 물론, 토큰 탈취 시도까지 방어하도록 설계했습니다. 동시에 사용자가 '아, 또 로그인해야 해?'라는 불편함을 느끼지 않도록, 보안성과 편의성 사이의 trade-off를 신경 쓰며 균형을 잡았습니다.
인증 시스템에서 가장 어려운 부분이 바로 이 균형을 찾는 일 같습니다. 보안을 지나치게 강조하면 사용자 경험이 불편해지고, 편의성에만 집중하면 보안이 취약해지기 때문입니다. 베어랜드가 고민했던 이 균형이 여러분의 프로젝트에도 작은 힌트가 되길 바랍니다.
'공부기록' 카테고리의 다른 글
[베어랜드 Auth 시스템] 1 - 쿠키와 세션 그리고 JWT (0) | 2025.03.01 |
---|---|
[트러블 슈팅] Cannot find module '...../bcrypt/lib/binding/napi-v3/bcrypt_lib.node' (1) | 2025.02.06 |
[시스템 아키텍처] 섹션 2: 시스템 요구 사항 및 아키텍처 드라이버 (1) | 2024.11.27 |
[시스템 아키텍처] (1) | 2024.11.26 |
[electron] electron-forge Cannot find module (0) | 2024.11.25 |