docs: JWT.sub 매칭 미구현 취약점 상세 문서화
- 본인 확인 검증 문제 정확한 플로우 추가 - JWT 토큰 구성 (Gmail/Slack) 상세 명시 - 취약 엔드포인트 목록 업데이트 - JWT.sub ≠ URL.user_id 검증 없음 명확화 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
61b374636e
commit
a8f3af04fb
@ -105,9 +105,67 @@ const scheduledTasks: ScheduledTask[] = [...]; // 하드코딩
|
|||||||
- localStorage 기반으로 다른 디바이스와 동기화 안됨
|
- localStorage 기반으로 다른 디바이스와 동기화 안됨
|
||||||
- WebSocket이나 polling 구현 없음
|
- WebSocket이나 polling 구현 없음
|
||||||
|
|
||||||
### 3.7 권한 검증 누락
|
### 3.7 🔴 권한 검증 누락 (본인 확인 미구현)
|
||||||
- user_preferences 수정 시 본인 확인 로직 필요
|
|
||||||
- 다른 사용자 설정 수정 가능한 보안 문제
|
#### 확인된 사실 (2025-09-15)
|
||||||
|
|
||||||
|
**전체 플로우:**
|
||||||
|
1. **Frontend** (`/home/admin/frontend-customer/src/components/activity-panel.tsx`)
|
||||||
|
- Line 85: `GET ${API_BASE}/api/preferences/${userId}`
|
||||||
|
- Line 155: `PUT ${API_BASE}/api/preferences/${userId}`
|
||||||
|
- userId는 localStorage에서 읽음 (Line 76)
|
||||||
|
- Authorization 헤더에 Bearer 토큰 포함 (Line 87, 158)
|
||||||
|
|
||||||
|
2. **Gateway** (`/home/admin/robeing-gateway/app/main.py`) - 51123:8100
|
||||||
|
- Line 365-391: `/api/preferences/{path}` 엔드포인트
|
||||||
|
- Line 369: `user_uuid = Depends(get_verified_user)` - JWT에서 UUID 추출
|
||||||
|
- Line 375-376 (GET): `http://192.168.219.52:9024/api/preferences/{path}` 프록시, 헤더 없음
|
||||||
|
- Line 377-382 (PUT):
|
||||||
|
- robeing-monitor로 프록시
|
||||||
|
- `X-User-Id: user_uuid` 헤더 추가 (Line 382)
|
||||||
|
- **문제점**: URL의 {path}와 JWT의 user_uuid 비교 없음
|
||||||
|
|
||||||
|
3. **robeing-monitor** (`/home/admin/ivada_project/robeing-monitor/app/api/monitor.py`) - 51124:9024
|
||||||
|
- Line 129: `async def get_preferences(user_id: str):`
|
||||||
|
- Line 194: `async def update_preferences(user_id: str, preferences: UserPreferencesUpdate):`
|
||||||
|
- 파라미터: URL의 {user_id}만 사용, Request 객체 없음
|
||||||
|
- **문제점**: JWT/헤더 검증 불가능
|
||||||
|
|
||||||
|
#### 취약점 핵심: JWT.sub ≠ URL.user_id 검증 없음
|
||||||
|
|
||||||
|
**문제**: URL의 user_id를 아무나 바꿔서 타인 데이터를 조회/수정 가능
|
||||||
|
- JWT의 sub(본인 UUID)과 URL의 user_id(대상 UUID)가 같은지 확인 안 함
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 정상 요청 (자기 데이터)
|
||||||
|
PUT /api/preferences/1e16e9d5-59f3-54da-a661-8abeabff4230
|
||||||
|
Authorization: Bearer [JWT with sub=1e16e9d5-59f3-54da-a661-8abeabff4230]
|
||||||
|
|
||||||
|
# 취약 요청 (타인 데이터 수정 가능)
|
||||||
|
PUT /api/preferences/b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0
|
||||||
|
Authorization: Bearer [JWT with sub=1e16e9d5-59f3-54da-a661-8abeabff4230]
|
||||||
|
# Gateway는 X-User-Id: 1e16e9d5를 전달하지만
|
||||||
|
# robeing-monitor는 URL의 b6ea2ee0를 사용
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 관련 서비스 JWT 검증 현황 (2025-09-15 확인)
|
||||||
|
|
||||||
|
**JWT 토큰 발급:**
|
||||||
|
- auth-server: JWT sub에 user.id(UUID) 사용 (`/home/admin/auth-server/app/providers/gmail.py` L198~, `slack.py` L329)
|
||||||
|
- 알고리즘: HS256, 만료: 30일 (`/home/admin/auth-server/app/core/auth.py`)
|
||||||
|
- Gmail OAuth JWT: `sub`(user UUID), `username`, `email`, `name`, `exp`(만료시간), `iat`(발급시간)
|
||||||
|
- Slack OAuth JWT: `sub`(user UUID), `email`, `name`, `username`, `picture`(프로필 이미지), `slack_user_id`, `slack_team_id`, `exp`, `iat`
|
||||||
|
|
||||||
|
**JWT 검증 구현된 서비스:**
|
||||||
|
- Gateway: 모든 주요 엔드포인트에서 `Depends(get_verified_user)` 사용
|
||||||
|
- rb8001: `/api/message`에서 JWT 검증, sub(UUID)를 user_id로 사용
|
||||||
|
|
||||||
|
**JWT.sub 매칭 없는 취약 엔드포인트:**
|
||||||
|
- Gateway: `/api/workspace/{user_id}`, `/api/workspace/assign` - Depends(get_verified_user) 없음
|
||||||
|
- robeing-monitor: Preferences API (`/api/preferences/{user_id}`) - 경로 user_id만 사용
|
||||||
|
- robeing-monitor: Gmail Items API - X-User-Id와 경로 user_id 일치만 확인, JWT.sub 매칭 없음
|
||||||
|
- rb10508_micro: `/memories/{user_id}`, `/user/{user_id}/history` - JWT 검증 없음
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user