실무에서 꼭 알아야 할 JWT 저장소 보안 패턴과 공격 탐지 방법

2025. 9. 13. 11:00·Frontend Development
728x90

안녕하세요! 프론트엔드/풀스택 실무에서 자주 부딪히는 “JWT를 어디에 보관할 것인가” 문제를 정리했습니다. 저장 위치별 보안/UX 트레이드오프, 실제로 일어나는 탈취(steal) 시나리오, 그리고 탈취 되었을 때 어떻게 눈치채고 대응할지까지 바로 적용 가능한 체크리스트로 담았습니다.

저장 위치별 비교

구분 localStorage sessionStorage Cookie (HttpOnly 권장)
접근성 JS로 window.localStorage 읽기/쓰기 탭 수명(탭 닫히면 소멸) JS 접근 불가(HttpOnly)·자동 전송(도메인/경로 일치 시)
지속성 브라우저 종료 후에도 유지 탭 살아있는 동안만 만료 시각/세션/영구 선택 가능
XSS에 대한 노출 높음(스크립트가 읽을 수 있음) 높음 낮음(HttpOnly면 읽기 불가)
CSRF 위험 낮음(자동 전송 안 함) 낮음 높음(자동 전송) → SameSite/CSRF 토큰 필요
도메인/경로 스코프 없음(도메인 전역에서 JS로 접근) 없음 세밀하게 설정 가능(domain/path)
구현 난이도 쉬움 쉬움 쿠키 옵션/CSRF 방어 설계 필요

요약 권장 패턴(현업 다수 채택)

  • Access Token: 메모리에만 보관(새로고침 때 사라져도 OK)
  • Refresh Token: HttpOnly + Secure + SameSite 쿠키로 저장 + Refresh Token Rotation
  • 이유: XSS로부터 Access Token 노출을 줄이고, CSRF는 쿠키 보안옵션과 토큰 방식으로 방어
  • 참고: local/sessionStorage를 꼭 써야 한다면, 짧은 만료 + 철저한 XSS 방어(CSP/Trusted Types)를 전제로만 고려

대표적 탈취 공격 시나리오

A. XSS(교차 사이트 스크립팅)

  • 대상: localStorage / sessionStorage / 비-HttpOnly 쿠키
  • 방법: 취약 페이지에 삽입된 스크립트가 localStorage.getItem('token') 등으로 탈취
  • 완화: 입력 검증/인코딩, CSP(스크립트 소스 화이트리스트), Trusted Types, 라이브러리 취약점 패치, HttpOnly 쿠키 사용

B. CSRF(사이트 간 요청 위조)

  • 대상: 쿠키 기반 인증(자동 전송)
  • 방법: 피해자 브라우저가 공격자 페이지에서 의도치 않은 인증 요청을 보냄
  • 완화: SameSite=Lax/Strict, CSRF 토큰(Double Submit / Synchronizer Token), 쿠키에 SameSite·Secure·HttpOnly, 쿠키 경로/도메인 최소화

C. 전송 구간 탈취(MITM)

  • 대상: 모든 저장 방식 (전송 시)
  • 방법: HTTPS 미사용, 중간자 공격으로 Authorization 헤더/쿠키 가로채기
  • 완화: HTTPS 강제(HSTS), TLS 최신 설정, 공용망 환경 주의

D. Refresh Token 재사용/재발급 악용

  • 대상: 장기 수명 Refresh Token
  • 방법: 한 번 유출되면 지속 액세스 토큰 발급 남용
  • 완화: Refresh Token Rotation(매번 교체 + 재사용 감지 시 즉시 무효), 짧은 수명 + 기기 바인딩(JTI/디바이스 ID), IP/디바이스 변동 감시

E. 로그·리퍼러·서드파티 유출

  • 대상: 토큰을 URL 쿼리/프래그먼트로 주고받을 때
  • 방법: 서버/클라이언트 로그에 남거나 Referer 헤더로 외부 도메인에 유출
  • 완화: 토큰을 URL에 절대 넣지 않기, 민감 값은 헤더/바디로만, 로깅 필터링

F. 브라우저 확장 프로그램/악성 SW

  • 대상: local/sessionStorage·비-HttpOnly 쿠키
  • 방법: 권한 과도한 확장/악성코드가 DOM/Storage 읽기
  • 완화: HttpOnly 쿠키, 보안 교육/정책, EDR/안티바이러스

G. 클릭재킹/XS-Leaks(간접 유출)

  • 대상: 쿠키 세션/특정 뷰
  • 완화: X-Frame-Options/frame-ancestors(CSP), 숨김 필드/타이밍·상태 유출 방어

탈취되었는지 어떻게 알까? (실무 탐지 시그널)

서버·백엔드 관점의 행동 기반 탐지가 핵심입니다.

액세스/리프레시 토큰 레벨

  • Refresh Token Rotation 재사용 감지: RT는 매 재발급마다 새 토큰으로 교체. 이전 RT가 다시 쓰이면 탈취로 간주 → 전체 세션 무효화 + 계정 보호 플로우 발동
  • JTI(토큰 고유 ID)·세션 테이블: 각 RT/세션에 고유 식별자 저장, 만료 전이라도 서버측 블랙리스트/리보크 가능

세션/행동 이상치

  • 불가능한 이동(Impossible Travel): 짧은 시간 내 물리적으로 불가능한 Geo-IP 이동, 이례적 ASN·프록시 사용
  • 디바이스/브라우저 지문 급변: User-Agent, 클라이언트 힌트, 해시된 지문 값 급변
  • 동시 사용 패턴: 동일 계정이 다중 지역/기기에서 동시에 액세스 토큰 재발급 시도
  • 이상 트래픽/레이트 초과: 비정상적인 로그인 시도, 재발급 엔드포인트로의 급증
  • 서명/클레임 무결성 실패: 서명 검증 실패, iss/aud/exp/nbf 등 클레임 이상치

클라이언트/프론트 관점(보조적)

  • “기기 로그아웃됨” 빈번, 사용자 알림/2FA 푸시가 계속 뜸
  • 저장소에 있던 토큰이 예상치 못하게 무효화(서버가 강제 리보크했을 가능성)

안전한 설계 패턴(권장 레시피)

패턴 A: “메모리 AT + HttpOnly RT”

  • Access Token(짧은 수명): 앱 메모리만 저장
  • Refresh Token(길게, Rotation): HttpOnly + Secure + SameSite=Lax/Strict 쿠키
  • 이점: XSS로 AT 직접 유출 어려움, CSRF는 쿠키 보안옵션+CSRF 토큰으로 방어
  • 주의: 새로고침 시 AT 사라지므로 Silent Refresh 흐름 구현 필요

패턴 B: 전부 쿠키 기반(AT/RT 둘 다 쿠키, AT는 짧게)

  • 전달: 쿠키 자동 전송
  • 필수: SameSite + CSRF 토큰(폼/헤더-기반 대조), 경로/도메인 최소화
  • 장점: SSR/MPA 친화
  • 단점: CSRF 설계 복잡도↑

(선택) 쿠키 하드닝 팁

  • HttpOnly, Secure, SameSite=Lax/Strict
  • __Host- 프리픽스(도메인 지정 금지+Secure+Path=/ 조합)
  • 민감 엔드포인트는 CORS 엄격·프리플라이트 필수

실전 방어 체크리스트

앱/프론트

  • 토큰을 URL에 절대 담지 않기
  • CSP(스크립트 소스 화이트리스트) + Trusted Types
  • 라이브러리/빌드 체인 취약점 정기 패치
  • 서드파티 스크립트·확장 권한 최소화

쿠키/세션

  • HttpOnly / Secure / SameSite 필수
  • __Host- 프리픽스 고려, 도메인/경로 최소 권한
  • CSRF 토큰(Double Submit / 상태값-검증형) + Referrer/Origin 검증

토큰 수명/회전

  • AT 매우 짧게(분 단위), RT는 Rotation + 재사용 탐지
  • jti/세션 테이블로 서버측 리보크 가능하게
  • 고위험 액션은 재인증/2FA 요구

네트워크/플랫폼

  • HTTPS 강제(HSTS), 최신 TLS
  • 레이트리밋·IP/ASN 평판 기반 차단
  • 보안 로깅(개인정보 마스킹), SIEM 연동

탈취 의심 시 “대응 프로토콜”

즉시 리스크 차단

  • 해당 계정의 모든 Refresh Token 무효화(세션 테이블/블랙리스트)
  • 재사용된 RT 발견 시 계정 보호 모드(강제 로그아웃, 비밀번호/2FA 재설정)

조사 & 범위 파악

  • 로그로 유출 시점·경로 추정(XSS, URL 누출, RT 재사용 IP 등)
  • 동일 패턴 다른 계정 영향 검사

취약점 핫픽스 + 재발 방지

  • CSP/TT 강화, 취약 입력 지점 패치, 토큰 수명·회전 정책 재점검

사용자 커뮤니케이션

  • 안전한 공지·가이드(의심 로그인 내역, 재인증 안내)

코드 스니펫 모음

(서버) 쿠키 설정 예시 (Node/Express)

res.cookie('__Host-rt', refreshToken, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',   // 민감도에 따라 'strict' 고려
  path: '/',         // __Host- 프리픽스는 path=/ 필수
  maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 등
});

(서버) CSRF Double-Submit 패턴 핵심

// 1) 로그인/초기 로드 시 CSRF 토큰 발급(쿠키와 응답 바디/헤더에 동시에)
res.cookie('csrf', csrfToken, { httpOnly: false, sameSite: 'lax', secure: true });

// 2) 클라이언트는 민감요청에 헤더로 전송
fetch('/api/transfer', {
  method: 'POST',
  headers: { 'X-CSRF-Token': readFromCookie('csrf') },
  body: JSON.stringify(payload),
});

// 3) 서버에서 쿠키값과 헤더값 일치 확인 + Origin/Referer 체크

(서버) Refresh Token Rotation 감지(개념)

// RT 테이블: { userId, tokenHash, revoked:boolean, createdAt, replacedById, lastUsedAt }
function rotateRefreshToken(oldRtId, userId) {
  // 1) oldRtId 사용해 새 RT 발급 + oldRt.replacedById = newRtId
  // 2) 요청 시 전달된 RT가 이미 replacedById를 가진 토큰이면 -> 재사용 탐지
  //    => 계정 보호 모드 + 전체 세션 리보크
}

결론: “완벽한 곳은 없다, 조합이 답”

XSS vs CSRF는 보안 축이 다릅니다. 대다수 SPA/SSR 서비스는 “AT는 메모리”, “RT는 HttpOnly 쿠키 + Rotation”으로 XSS·CSRF를 동시에 관리합니다. 여기에 짧은 토큰 수명, CSP/Trusted Types, SIEM 기반 이상행동 탐지를 결합할 때 탈취 확률은 낮아지고, 탈취 시에도 빠르게 감지·차단할 수 있습니다.


핵심 요약

  • 보관 위치 결론: AT는 메모리, RT는 HttpOnly 쿠키(+Rotation)
  • XSS vs CSRF: 서로 다른 축이므로 함께 고려
  • 핵심 방어: SameSite/CSRF 토큰, 짧은 수명, Rotation, CSP/TT, HSTS/TLS
  • 탐지 시그널: RT 재사용, 불가능한 이동, 지문 급변, 레이트 초과
  • 대응 프로토콜: 전 계정 RT 무효화 → 조사 → 핫픽스 → 사용자 안내
728x90
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

React 리렌더링(Re-rendering): Trigger → Render → Commit  (0) 2025.10.03
Next.js SSR 페이지 풀 페이지 캐싱  (0) 2025.09.08
useEffect에서 setInterval이 상태를 못 따라오는 이유 (stale closure)  (0) 2025.09.07
React Error Boundary: 왜 아직도 클래스일까?  (0) 2025.09.07
setTimeout vs Promise.then vs queueMicrotask  (0) 2025.09.05
'Frontend Development' 카테고리의 다른 글
  • React 리렌더링(Re-rendering): Trigger → Render → Commit
  • Next.js SSR 페이지 풀 페이지 캐싱
  • useEffect에서 setInterval이 상태를 못 따라오는 이유 (stale closure)
  • React Error Boundary: 왜 아직도 클래스일까?
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (133)
      • Frontend Development (62)
      • Backend Development (25)
      • Algorithm (33)
        • 백준 (11)
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
      • Language (6)
        • JavaScript (6)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    tailwindcss
    frontend development
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
실무에서 꼭 알아야 할 JWT 저장소 보안 패턴과 공격 탐지 방법
상단으로

티스토리툴바