DOCS/ideas/250815_JWT_토큰_검증_보안_개선_방안.md
happybell80 b3822e189d docs: JWT 토큰 검증 보안 개선 - 계획에서 아이디어로 변경
- 결정해야 할 사항이 많아 추가 검토 필요
- plans → ideas 폴더로 이동

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-15 14:52:46 +09:00

8.4 KiB

JWT 토큰 검증 보안 개선 방안

작성일: 2025-08-15
작성자: 51123 서버, 51124 서버, 로컬 개발자
상태: 아이디어 → 추가 검토 필요
우선순위: 🔴 Critical (보안 취약점)
관련: auth-server, robing services (rb8001, rb10408, rb10508), frontend

개요

JWT 토큰이 발급되지만 실제 API 호출 시 검증되지 않는 Critical 보안 취약점을 해결하기 위한 단계별 실행 계획입니다. 하이브리드 접근법(Phase 1: 최소 수정 → Phase 2: 프록시 패턴)을 통해 즉각적인 보안 개선과 장기적 아키텍처 개선을 동시에 추진합니다.

구현 방식 결정

하이브리드 접근법 채택

  • Phase 1 (즉시): Option 1 - JWT_SECRET 공유로 빠른 구현
  • Phase 2 (1주 후): Option 2 - auth-server 프록시 패턴으로 전환

기술적 결정 사항

확정 사항

항목 결정 내용 근거
JWT_SECRET 새로 생성 (32자 이상) 보안 강화
토큰 만료 시간 Access: 2시간 보안-편의성 균형
Refresh Token Phase 2에서 구현 초기 복잡도 감소
에러 응답 형식 표준화된 JSON 일관성
X-User-Id 헤더 2주간 유지 후 제거 호환성 보장

에러 응답 표준

{
  "error": "unauthorized",
  "message": "Invalid or expired token",
  "code": "AUTH_001",
  "status": 401
}

Phase 1: 최소 수정 (D-Day ~ D+2)

Day 0: 환경 준비 및 Frontend 수정

51123 서버 작업

# JWT_SECRET 생성 및 설정
openssl rand -base64 32
# 결과를 모든 서버의 .env에 동일하게 설정

로컬 개발자 작업

  1. robing-api.ts 수정
export async function sendMessage(
  text: string, 
  token: string, 
  userId: string
): Promise<MessageResponse> {
  const response = await fetch(`${ROBING_API_URL}/api/chat`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,  // JWT 토큰 추가
      'X-User-Id': userId  // 호환성 유지 (2주 후 제거)
    },
    body: JSON.stringify({ message: text, user_id: userId })
  });
  return response.json();
}
  1. chat-interface.tsx 수정
import { useAuth } from '@/contexts/auth-context';

const ChatInterface = () => {
  const { user, isAuthenticated } = useAuth();
  
  const handleSend = async (input: string) => {
    if (!isAuthenticated || !user) {
      // 로그인 페이지로 리다이렉트
      return;
    }
    
    const token = localStorage.getItem('token');  // 'token' 키 확인됨
    const response = await sendMessage(input, token, user.id);
  };
};

Day 1: Backend JWT 검증 구현

51124 서버 작업

  1. JWT 검증 미들웨어 생성 (각 로빙 서비스에 추가)
# app/core/auth.py
from jose import jwt, JWTError
from fastapi import HTTPException, Request
import os

JWT_SECRET = os.getenv("JWT_SECRET")
JWT_ALGORITHM = "HS256"

async def verify_jwt_token(request: Request):
    """JWT 토큰 검증 미들웨어"""
    # Phase 1: X-User-Id 폴백 지원 (2주간)
    x_user_id = request.headers.get("X-User-Id")
    
    # Authorization 헤더 확인
    auth_header = request.headers.get("Authorization", "")
    
    if not auth_header.startswith("Bearer "):
        if x_user_id:  # 임시 폴백
            request.state.user_id = x_user_id
            return x_user_id
        raise HTTPException(status_code=401, detail="Token missing")
    
    token = auth_header.replace("Bearer ", "")
    
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
        user_id = payload.get("sub", "default")
        request.state.user_id = user_id
        return user_id
    except JWTError as e:
        if x_user_id:  # 임시 폴백
            request.state.user_id = x_user_id
            return x_user_id
        raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")
  1. 각 서비스에 미들웨어 적용
# main.py 수정
from app.core.auth import verify_jwt_token

@app.post("/api/chat")
async def chat(request: Request):
    user_id = await verify_jwt_token(request)
    # 기존 로직 계속...
  1. requirements.txt 업데이트
python-jose[cryptography]==3.3.0

Day 2: 통합 테스트 및 모니터링

테스트 시나리오

  1. Critical Tests (필수)

    • 정상 토큰으로 요청 → 200 OK
    • 잘못된 토큰으로 요청 → 401 Unauthorized
    • 토큰 없이 요청 (X-User-Id만) → 200 OK (폴백)
    • 만료된 토큰으로 요청 → 401 Unauthorized
  2. 모니터링 설정

# 로깅 추가
import logging

logger = logging.getLogger(__name__)

async def verify_jwt_token(request: Request):
    # ... 검증 로직 ...
    
    if auth_failed:
        logger.warning(f"Auth failed: IP={request.client.host}, User-Agent={request.headers.get('User-Agent')}")

Phase 2: 프록시 패턴 전환 (D+7)

auth-server 프록시 구현 (51123)

# auth-server에 프록시 엔드포인트 추가
@app.api_route("/api/proxy/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy_request(
    path: str,
    request: Request,
    user_id: str = Depends(verify_jwt)
):
    """모든 API 요청을 검증 후 전달"""
    headers = dict(request.headers)
    headers["X-Verified-User-Id"] = user_id
    headers.pop("Authorization", None)  # 내부 통신에서는 제거
    
    # 51124 서버로 전달
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method=request.method,
            url=f"http://192.168.219.52:8000/api/{path}",
            headers=headers,
            content=await request.body()
        )
    
    return response.json()

Nginx 설정 변경 (51123)

# /etc/nginx/sites-available/ro-being
location /api/ {
    proxy_pass http://localhost:9000/api/proxy/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

리스크 관리

완화 전략

리스크 영향도 완화 방안 담당
Frontend 배포 지연 높음 X-User-Id 폴백 2주 유지 로컬
JWT_SECRET 노출 매우 높음 .env 관리, .gitignore 확인 전체
성능 저하 중간 JWT 검증 결과 캐싱 (Phase 2) 51124
토큰 만료 UX 중간 Refresh Token (Phase 2) 51123

롤백 계획

# 환경변수로 JWT 검증 on/off
JWT_VERIFICATION_ENABLED = os.getenv("JWT_VERIFICATION_ENABLED", "true") == "true"

async def verify_jwt_token(request: Request):
    if not JWT_VERIFICATION_ENABLED:
        # 기존 방식으로 폴백
        return request.headers.get("X-User-Id", "default")
    # ... JWT 검증 로직 ...

성공 지표

Phase 1 (D+3 측정)

  • 인증 실패율 < 1% (정상 사용자)
  • 평균 응답시간 증가 < 10ms
  • 보안 취약점 해결 확인

Phase 2 (D+10 측정)

  • 프록시 경유 성공률 > 99.9%
  • 평균 지연시간 < 20ms
  • X-User-Id 헤더 제거 완료

작업 추적

Phase 1 체크리스트

  • JWT_SECRET 생성 및 배포 (51123)
  • Frontend Authorization 헤더 추가 (로컬)
  • rb8001 JWT 검증 추가 (51124)
  • rb10408 JWT 검증 추가 (51124)
  • rb10508 JWT 검증 추가 (51124)
  • 통합 테스트 완료 (전체)
  • 모니터링 대시보드 확인 (51123)

Phase 2 체크리스트

  • auth-server 프록시 엔드포인트 개발 (51123)
  • Nginx 설정 변경 (51123)
  • 내부 통신 보안 검증 (51124)
  • X-User-Id 헤더 제거 (로컬)
  • Refresh Token 구현 (51123)
  • 최종 보안 감사 (전체)

커뮤니케이션 계획

일일 동기화

  • 시간: 매일 오전 10시
  • 채널: 개발 채팅방
  • 내용: 진행 상황, 이슈, 다음 작업

긴급 이슈 에스컬레이션

  1. 보안 취약점 추가 발견 시
  2. 서비스 장애 발생 시
  3. 롤백 필요 판단 시

문서화 요구사항

완료 후 작성

  1. API 명세 업데이트 (Authorization 헤더 필수)
  2. 트러블슈팅 가이드 (인증 실패 디버깅)
  3. 운영 가이드 (JWT_SECRET 관리)
  4. 보안 감사 보고서

"보안은 프로세스입니다. 한 번의 수정이 아닌 지속적인 개선이 필요합니다."

이 계획을 통해 즉각적인 보안 개선과 장기적인 아키텍처 개선을 동시에 달성합니다.