Post

인증과 권한 흐름: 세션 vs JWT

Authentication·Authorization 분리, 세션/쿠키/CSRF, JWT 선택 기준을 정리

인증과 권한 흐름: 세션 vs JWT

결론 요약

  • 인증(Authentication)은 “너가 누구인지 확인”이고, 권한(Authorization)은 “무엇을 할 수 있는지 제한”이다.
  • 세션 기반 인증은 서버가 상태(state)를 가진다는 전제 위에서 안전하지만, CSRF에 취약하다.
  • JWT는 무상태(stateless)로 확장에 유리하지만, 폐기·저장 위치를 잘못 선택하면 보안이 급격히 나빠진다.
  • 인증 설계는 라이브러리 선택 문제가 아니라 위협 모델(threat model)과 요청 흐름 설계 문제다.
  • “언제 JWT를 쓰면 망하는지”를 말할 수 있어야 올바른 선택이다.

인증을 ‘동작 흐름’으로 이해하기

세션 / 쿠키 / CSRF 그리고 JWT 전환점


1️⃣ 정의: 인증과 권한을 먼저 분리하라

구분의미질문
Authentication신원 확인“너 누구야?”
Authorization권한 검증“이 행동 해도 돼?”

이 둘을 섞으면 다음 문제가 생긴다.

  • 로그인 여부만으로 관리자 행동 허용
  • 토큰 검증은 했지만 권한 체크 누락

결론: 인증은 “입장권”, 권한은 “좌석 등급”이다. 처리 시점과 책임이 다르다.


2️⃣ 쿠키와 세션: 역할과 차이를 구조로 이해

쿠키(Cookie)

  • 저장 위치: 브라우저
  • 역할: 요청마다 자동 전송되는 작은 데이터
  • 용도: 세션 식별자, 설정 값

세션(Session)

  • 저장 위치: 서버(DB/메모리/Redis)
  • 역할: 로그인 상태 유지
  • 식별자: 세션 ID(쿠키에 저장)

비교 표

항목쿠키세션
저장 위치클라이언트서버
민감 정보❌ 직접 저장 금지✅ 저장 가능
만료브라우저/시간서버 정책
확장성높음서버 자원 필요
CSRF취약취약(쿠키 기반)

핵심은 이것이다. 세션은 안전하지만, 쿠키를 통해 식별되기 때문에 CSRF 표적이 된다.


3️⃣ 로그인 흐름: 요청 시퀀스로 보기

세션 기반 로그인 흐름 (텍스트 다이어그램)

1
2
3
4
5
6
7
8
9
10
11
12
13
[Client]
  └─ POST /login (id, password)
        ↓
[Server]
  ├─ 사용자 검증
  ├─ 세션 생성 (sessionId)
  └─ Set-Cookie: sessionId
        ↓
[Client]
  └─ 이후 모든 요청에 Cookie 자동 전송
        ↓
[Server]
  └─ sessionId로 사용자 식별

특징

  • 브라우저가 쿠키를 자동 전송
  • 서버는 상태를 기억

4️⃣ CSRF: 왜 세션 기반에서 특히 문제인가

CSRF란 무엇인가

  • 사용자가 로그인된 상태를 악용해
  • 의도하지 않은 요청을 다른 사이트에서 전송하게 만드는 공격

왜 세션 기반이 취약한가

  • 쿠키는 출처와 무관하게 자동 전송
  • 공격자는 “요청을 보내게”만 하면 된다

해결 개념: CSRF Token

  • 서버가 예측 불가능한 토큰을 발급
  • 클라이언트는 요청 바디/헤더에 토큰 포함
  • 서버는 토큰 일치 여부 검증

핵심: “이 요청이 정말 이 화면에서 만들어졌는가?”를 검증


5️⃣ JWT: 왜 등장했고, 언제 쓰면 망하는가

JWT(JSON Web Token)의 핵심 아이디어

  • 인증 정보를 서버가 아니라 토큰 자체에 포함
  • 서버는 토큰 검증만 수행 (무상태)

장점

  • 서버 확장 용이
  • API 서버/마이크로서비스에 적합
  • 모바일/외부 클라이언트 친화적

단점 (여기서 대부분 실패)

문제설명
폐기 불가유출 시 만료 전까지 유효
저장 위치localStorage → XSS 위험
크기요청마다 전송 → 비용
상태 관리로그아웃/강제 차단 어려움

“JWT 쓰면 망하는 경우”

  • 브라우저 기반 서비스인데 localStorage 저장
  • 짧은 만료 전략 없이 장기 토큰 사용
  • 권한 정보까지 토큰에 과다 포함

결론 JWT는 “편해서” 쓰는 게 아니라 “무상태가 필수인 구조”에서만 써야 한다.


6️⃣ toy 예시: 로그인 / 로그아웃

세션 기반 로그인

1
2
POST /login → 세션 생성 → 쿠키 저장
POST /logout → 세션 삭제 → 쿠키 무효화

JWT 기반 로그인

1
2
POST /login → JWT 발급
Client 저장 → Authorization 헤더 전송

차이는 “상태를 누가 기억하는가”다.


7️⃣ 실무 예시: 관리자만 상품 수정

잘못된 접근

1
로그인 여부만 확인 → 상품 수정 허용

올바른 흐름

  1. 인증: 사용자 식별
  2. 권한: role === ‘admin’ 검증
  3. 행위 실행
1
2
if (!user) return 401;
if (user.role !== 'admin') return 403;
  • 401: 인증 실패
  • 403: 권한 부족

상태 코드도 의미에 맞게 분리해야 한다.


8️⃣ 언제 세션, 언제 JWT인가 (실무 결론)

세션이 유리한 경우

  • 서버 렌더링(SSR)
  • 브라우저 중심 서비스
  • 강제 로그아웃/권한 변경 잦음

JWT가 유리한 경우

  • 모바일/외부 클라이언트
  • API 서버
  • 수평 확장 필수 구조

인증은 “트렌드”가 아니라 요구사항의 함수다.


보안 체크리스트 (쿠키 옵션 포함)

  • 인증과 권한을 분리해서 처리한다
  • 세션 ID만 쿠키에 저장한다
  • 쿠키에 HttpOnly 설정
  • 쿠키에 Secure 설정(HTTPS)
  • SameSite 정책을 의도적으로 선택했다
  • CSRF 토큰을 사용한다(세션 기반)
  • JWT 만료 시간을 짧게 설정했다
  • JWT 저장 위치를 명확히 결정했다
  • 권한 체크를 모든 보호 라우트에 적용했다
  • 401과 403을 의미에 맞게 사용한다

This post is licensed under CC BY 4.0 by the author.