# JWT 토큰 검증 보안 개선 방안

작성일: 2025-08-15  
작성자: 51123 서버, 51124 서버, 로컬 개발자  
상태: 아이디어 → 추가 검토 필요  
우선순위: 🔴 Critical (보안 취약점)  
관련: auth-server, robeing 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주간 유지 후 제거 | 호환성 보장 |

### 에러 응답 표준
```json
{
  "error": "unauthorized",
  "message": "Invalid or expired token",
  "code": "AUTH_001",
  "status": 401
}
```

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

### Day 0: 환경 준비 및 Frontend 수정

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

#### 로컬 개발자 작업
1. **robeing-api.ts 수정**
```typescript
export async function sendMessage(
  text: string, 
  token: string, 
  userId: string
): Promise<MessageResponse> {
  const response = await fetch(`${ROBEING_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();
}
```

2. **chat-interface.tsx 수정**
```typescript
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 검증 미들웨어 생성** (각 로빙 서비스에 추가)
```python
# 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)}")
```

2. **각 서비스에 미들웨어 적용**
```python
# 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)
    # 기존 로직 계속...
```

3. **requirements.txt 업데이트**
```
python-jose[cryptography]==3.3.0
```

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

#### 테스트 시나리오

1. **Critical Tests (필수)**
   - [x] 정상 토큰으로 요청 → 200 OK
   - [x] 잘못된 토큰으로 요청 → 401 Unauthorized
   - [x] 토큰 없이 요청 (X-User-Id만) → 200 OK (폴백)
   - [x] 만료된 토큰으로 요청 → 401 Unauthorized

2. **모니터링 설정**
```python
# 로깅 추가
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)

```python
# 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)

```nginx
# /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 |

### 롤백 계획

```python
# 환경변수로 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. 보안 감사 보고서

---

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

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