From 7fca722026b0e142ca41783b0354f90c2a1a00b8 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Wed, 27 Aug 2025 14:26:00 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20JWT=20=EC=9D=B8=EC=A6=9D=20=EC=99=84?= =?UTF-8?q?=EC=A0=84=20=EB=AF=B8=EA=B5=AC=ED=98=84=20CRITICAL=20=EB=B3=B4?= =?UTF-8?q?=EC=95=88=20=EC=B7=A8=EC=95=BD=EC=A0=90=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 전체 시스템 JWT 검증 미구현 상태 분석 - 실제 공격 시연 4가지 시나리오 포함 - CVE 스코어 9.8 극도로 위험한 취약점 - 즉시 조치 방안 및 코드 예시 제공 - 단계별 해결 일정 (D-Day ~ D+30) 영향 범위: 51123, 51124 모든 서비스 위험 수준: CRITICAL - 즉시 조치 필요 --- ...27_JWT_인증_완전_미구현_CRITICAL.md | 427 ++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 troubleshooting/250827_JWT_인증_완전_미구현_CRITICAL.md diff --git a/troubleshooting/250827_JWT_인증_완전_미구현_CRITICAL.md b/troubleshooting/250827_JWT_인증_완전_미구현_CRITICAL.md new file mode 100644 index 0000000..e7cb8ea --- /dev/null +++ b/troubleshooting/250827_JWT_인증_완전_미구현_CRITICAL.md @@ -0,0 +1,427 @@ +# JWT 인증 완전 미구현 - CRITICAL 보안 취약점 + +## 작성일: 2025-08-27 +## 작성자: 51123 서버 관리자 +## 상태: 🔴 CRITICAL - 즉시 조치 필요 +## 영향 범위: 전체 시스템 (51123, 51124 모든 서비스) +## 위험 수준: 극도로 높음 + +--- + +## 1. 문제 요약 + +### 🚨 핵심 문제 +**현재 시스템은 JWT 인증이 완전히 무력화된 상태입니다.** +- 누구나 인증 없이 모든 API 호출 가능 +- 아무 user_id로 타인 행세 가능 +- 타 사용자 데이터 접근 가능 + +### 영향받는 서비스 +- **51123 서버**: auth-server, robeing-gateway +- **51124 서버**: rb8001, rb10508, rb10408, skill-email, skill-news + +--- + +## 2. 취약점 상세 분석 + +### 2.1 auth-server (51123) +```python +# /home/admin/auth-server/app/core/auth.py +# ✅ JWT 생성/검증 코드는 존재 +JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-this") + +def create_access_token(data: Dict[str, Any]) -> str: + # JWT 토큰 생성 로직 구현됨 + +def decode_access_token(token: str) -> Dict[str, Any]: + # JWT 토큰 검증 로직 구현됨 +``` + +**문제점**: +- JWT 발급 코드는 있지만 실제 사용되지 않음 +- 각 서비스가 이 토큰을 검증하지 않음 + +### 2.2 robeing-gateway (51123:8100) +```python +# /home/admin/robeing-gateway/app/main.py:40-65 +def get_verified_user(authorization: Optional[str] = Header(None)): + if not authorization or not authorization.startswith("Bearer "): + return "default" # 🔴 JWT 없어도 통과! + + try: + token = authorization.replace("Bearer ", "") + payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"]) + # ... + except JWTError as e: + logger.error(f"JWT verification failed: {e}") + return "default" # 🔴 잘못된 JWT도 통과! +``` + +**문제점**: +- JWT 검증 실패 시 에러 반환이 아닌 "default" 사용자로 처리 +- 인증 우회 가능 + +### 2.3 rb8001 (51124:8001) +```python +# /home/admin/ivada_project/rb8001/main.py:51-70 +@app.post("/api/message") +async def message_endpoint(request: MessageRequest, req: Request): + # X-User-Id 헤더에서 사용자 ID 가져오기 + user_id = request.user_id + if not user_id: + user_id = req.headers.get("X-User-Id") # 🔴 헤더만 확인! + + if not user_id: + user_id = "web_user" # 🔴 기본값 사용! +``` + +**문제점**: +- JWT 검증 코드 전혀 없음 +- X-User-Id 헤더만 확인 (조작 가능) +- python-jose 라이브러리 미설치 + +--- + +## 3. 실제 공격 시연 + +### 3.1 테스트 환경 +- 테스트 일시: 2025-08-27 14:12 +- 테스트 위치: 51123 서버 +- 대상 서비스: rb8001, robeing-gateway + +### 3.2 공격 시나리오 및 결과 + +#### 시나리오 1: JWT 토큰 없이 직접 API 호출 +```bash +curl -X POST http://192.168.219.52:8001/api/message \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello test", "user_id": "test_user"}' + +# 결과: ✅ 성공 +{ + "user_id": "test_user", + "bot_response": "안녕하세요, 김종태님! 로빙입니다.", + "status": "success" +} +``` + +#### 시나리오 2: 가짜 JWT 토큰 사용 +```bash +curl -X POST http://192.168.219.52:8001/api/message \ + -H "Authorization: Bearer fake-invalid-token-123" \ + -d '{"text": "Hello test with fake token", "user_id": "hacker"}' + +# 결과: ✅ 성공 (가짜 토큰이 무시됨) +{ + "user_id": "hacker", + "bot_response": "안녕하세요, 김종태님! 로빙입니다.", + "status": "success" +} +``` + +#### 시나리오 3: X-User-Id 헤더 조작 +```bash +curl -X POST http://192.168.219.52:8001/api/message \ + -H "X-User-Id: another_hacker" \ + -d '{"text": "Test with X-User-Id header only"}' + +# 결과: ✅ 성공 (아무 user_id나 사용 가능) +{ + "user_id": "another_hacker", + "bot_response": "안녕하세요, 김종태님!", + "status": "success" +} +``` + +#### 시나리오 4: Gateway 우회 테스트 +```bash +curl -X POST http://localhost:8100/api/chat \ + -H "Authorization: Bearer completely-fake-token-xyz" \ + -d '{"text": "Hello with fake JWT", "user_id": "hacker"}' + +# 결과: ✅ 성공 (Gateway도 우회됨) +{ + "user_id": "default", + "bot_response": "안녕하세요, 사용자님.", + "status": "success" +} +``` + +--- + +## 4. 보안 위험 평가 + +### 4.1 위험 수준 분류 + +| 취약점 | 심각도 | CVE 스코어 | 설명 | +|--------|--------|-----------|------| +| **인증 우회** | 🔴 Critical | 9.8 | 누구나 인증 없이 API 호출 가능 | +| **권한 상승** | 🔴 Critical | 9.1 | 일반 사용자가 타인으로 위장 가능 | +| **데이터 노출** | 🔴 Critical | 8.6 | 타 사용자의 대화 내역 접근 가능 | +| **세션 하이재킹** | 🔴 Critical | 8.3 | 타인의 세션 탈취 및 조작 가능 | +| **리소스 남용** | 🟡 High | 7.5 | 무제한 API 호출로 서버 부하 | + +### 4.2 잠재적 공격 시나리오 + +1. **데이터 탈취** + - 공격자가 다른 사용자의 user_id로 대화 내역 조회 + - Gmail 토큰 등 민감 정보 접근 + +2. **서비스 남용** + - 무제한 API 호출로 서버 리소스 고갈 + - LLM API 비용 폭증 + +3. **사용자 위장** + - 타인으로 위장하여 잘못된 정보 입력 + - 신뢰도 훼손 + +4. **데이터 오염** + - ChromaDB에 악의적 데이터 주입 + - 학습 데이터 오염 + +--- + +## 5. 근본 원인 분석 + +### 5.1 설계 단계 문제 +- JWT 인증 시스템 설계는 되었으나 구현 미완료 +- 개발 편의를 위한 임시 우회 코드가 프로덕션에 배포 + +### 5.2 구현 단계 문제 +- 각 마이크로서비스별 JWT 검증 미들웨어 미구현 +- python-jose 등 필요 라이브러리 미설치 +- 환경변수 JWT_SECRET_KEY 미설정 또는 불일치 + +### 5.3 테스트 단계 문제 +- 보안 테스트 부재 +- JWT 검증 관련 단위 테스트 없음 +- 통합 테스트에서 인증 검증 누락 + +### 5.4 배포 단계 문제 +- 보안 체크리스트 미적용 +- 코드 리뷰에서 보안 검증 누락 +- 프로덕션 배포 전 보안 감사 미실시 + +--- + +## 6. 즉시 조치 방안 + +### 6.1 긴급 조치 (D-Day) + +#### Step 1: JWT_SECRET_KEY 생성 및 배포 +```bash +# 51123 서버에서 실행 +openssl rand -base64 32 +# 결과: XKj8n2bL9Qa1pRt5vY6wC3dF7gH0iM4sN8oP2qU5xZ= + +# 모든 서비스의 .env 파일에 추가 +JWT_SECRET_KEY=XKj8n2bL9Qa1pRt5vY6wC3dF7gH0iM4sN8oP2qU5xZ= +``` + +#### Step 2: 각 서비스에 JWT 검증 미들웨어 추가 + +**rb8001 수정 예시**: +```python +# /home/admin/ivada_project/rb8001/app/auth.py (새 파일) +from jose import jwt, JWTError +from fastapi import HTTPException, Request, status +import os + +JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY") +JWT_ALGORITHM = "HS256" + +async def verify_jwt_token(request: Request) -> str: + """JWT 토큰 검증 미들웨어""" + auth_header = request.headers.get("Authorization", "") + + if not auth_header.startswith("Bearer "): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Missing or invalid authorization header" + ) + + token = auth_header.replace("Bearer ", "") + + try: + payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM]) + user_id = payload.get("sub") + + if not user_id: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid token payload" + ) + + return user_id + + except JWTError as e: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=f"Token validation failed: {str(e)}" + ) + +# main.py 수정 +from app.auth import verify_jwt_token +from fastapi import Depends + +@app.post("/api/message") +async def message_endpoint( + request: MessageRequest, + user_id: str = Depends(verify_jwt_token) # JWT 검증 적용 +): + # 이제 user_id는 검증된 값 + # 기존 로직 계속... +``` + +#### Step 3: Gateway 수정 +```python +# robeing-gateway/app/main.py 수정 +def get_verified_user(authorization: Optional[str] = Header(None)): + if not authorization or not authorization.startswith("Bearer "): + raise HTTPException(401, "Authorization header missing") # 변경 + + try: + token = authorization.replace("Bearer ", "") + payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"]) + username = payload.get("sub") + + if not username: + raise HTTPException(401, "Invalid token payload") # 변경 + + return username + + except JWTError as e: + raise HTTPException(401, f"JWT verification failed: {e}") # 변경 +``` + +### 6.2 단기 조치 (D+1 ~ D+3) + +1. **의존성 설치** +```bash +# 각 서비스 디렉토리에서 +pip install python-jose[cryptography] +# requirements.txt 업데이트 +``` + +2. **환경변수 통일** +- 모든 서비스의 .env 파일에 동일한 JWT_SECRET_KEY 설정 +- Docker Compose 환경변수 업데이트 + +3. **테스트 구현** +- JWT 검증 단위 테스트 +- 인증 실패 시나리오 테스트 +- 통합 테스트 업데이트 + +### 6.3 중장기 조치 (D+7 ~ D+30) + +1. **중앙 인증 서버 구축** + - auth-server를 모든 인증의 single source of truth로 + - 토큰 검증 API 엔드포인트 제공 + +2. **Refresh Token 구현** + - Access Token: 2시간 + - Refresh Token: 30일 + - 자동 갱신 로직 + +3. **API Gateway 패턴 강화** + - 모든 요청이 Gateway 경유 + - Gateway에서 중앙 집중식 인증 + - 내부 서비스 간 mTLS + +4. **보안 감사 체계** + - 정기적 penetration testing + - 자동화된 보안 스캔 + - 보안 체크리스트 의무화 + +--- + +## 7. 검증 방법 + +### 7.1 수정 후 테스트 +```bash +# 1. JWT 없이 호출 → 실패 예상 +curl -X POST http://192.168.219.52:8001/api/message \ + -H "Content-Type: application/json" \ + -d '{"text": "test"}' +# 예상: 401 Unauthorized + +# 2. 잘못된 JWT → 실패 예상 +curl -X POST http://192.168.219.52:8001/api/message \ + -H "Authorization: Bearer invalid-token" \ + -d '{"text": "test"}' +# 예상: 401 Unauthorized + +# 3. 유효한 JWT → 성공 예상 +curl -X POST http://192.168.219.52:8001/api/message \ + -H "Authorization: Bearer ${VALID_JWT_TOKEN}" \ + -d '{"text": "test"}' +# 예상: 200 OK +``` + +### 7.2 모니터링 지표 +- 401 에러 증가율 +- API 호출 패턴 변화 +- 비정상 user_id 패턴 + +--- + +## 8. 예상 영향 + +### 8.1 긍정적 영향 +- 보안 취약점 해결 +- 사용자 데이터 보호 +- 시스템 신뢰도 향상 + +### 8.2 부정적 영향 (일시적) +- 기존 통합 중단 가능성 +- Frontend 업데이트 필요 +- 사용자 재로그인 필요 + +### 8.3 완화 방안 +- 단계적 롤아웃 +- X-User-Id 헤더 2주간 폴백 지원 +- 상세한 에러 메시지 제공 + +--- + +## 9. 책임 및 일정 + +### 9.1 담당자 지정 +- **서버 관리자**: JWT_SECRET_KEY 생성 및 배포 +- **개발자**: 각 서비스 JWT 검증 구현 +- **QA**: 보안 테스트 수행 +- **DevOps**: 배포 및 모니터링 + +### 9.2 일정 +| 일자 | 작업 | 담당 | 상태 | +|------|------|------|------| +| D-Day (08/27) | JWT_SECRET 생성 및 환경변수 설정 | 서버 관리자 | 대기 | +| D+1 (08/28) | rb8001 JWT 검증 구현 | 개발자 | 대기 | +| D+2 (08/29) | 나머지 서비스 JWT 검증 구현 | 개발자 | 대기 | +| D+3 (08/30) | 통합 테스트 및 배포 | QA/DevOps | 대기 | +| D+7 (09/03) | 보안 감사 | 보안팀 | 대기 | + +--- + +## 10. 참고 문서 + +- [JWT 토큰 검증 보안 개선 방안](./250815_JWT_토큰_검증_보안_개선_방안.md) +- [OWASP Top 10 - Broken Authentication](https://owasp.org/www-project-top-ten/) +- [JWT Best Practices](https://tools.ietf.org/html/rfc8725) +- [python-jose Documentation](https://python-jose.readthedocs.io/) + +--- + +## 11. 결론 + +**현재 시스템은 인증이 완전히 무력화된 극도로 위험한 상태입니다.** + +이는 단순한 버그가 아닌 시스템 전체의 보안을 위협하는 Critical 취약점으로, 즉시 모든 개발을 중단하고 이 문제 해결에 집중해야 합니다. + +JWT 검증 구현은 선택이 아닌 필수이며, 이를 통해서만 사용자 데이터 보호와 시스템 무결성을 보장할 수 있습니다. + +--- + +*작성 완료: 2025-08-27 14:30* +*최종 검토: 51123 서버 관리자* +*다음 업데이트: 조치 시작 후 즉시* \ No newline at end of file