로그인 인증 시스템 보안 취약점 분석 문서 추가
- JWT 토큰 발급은 되지만 검증 안 되는 문제 발견 - X-User-Id 헤더 조작 가능한 심각한 보안 취약점 - Frontend, robing-gateway, auth-server 간 연결 끊김 - 긴급 수정 필요사항 및 구현 방안 제시 - Critical 수준의 보안 위험으로 즉시 조치 필요
This commit is contained in:
parent
6b2574d454
commit
1e38639d46
212
ideas/250815_로그인_인증_시스템_보안_개선_필요사항.md
Normal file
212
ideas/250815_로그인_인증_시스템_보안_개선_필요사항.md
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
# 로그인 인증 시스템 보안 개선 필요사항
|
||||||
|
|
||||||
|
작성일: 2025-08-15
|
||||||
|
작성자: Claude & happybell80
|
||||||
|
상태: 아이디어 → 긴급 개선 필요
|
||||||
|
관련: auth-server, robing-gateway, frontend
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
현재 로빙 프로젝트의 인증 시스템에서 JWT 토큰은 발급되지만 실제 API 호출 시 검증되지 않는 심각한 보안 취약점이 발견되었습니다. 누구나 X-User-Id 헤더를 조작하여 다른 사용자로 위장할 수 있는 상태입니다.
|
||||||
|
|
||||||
|
## 현재 인증 흐름
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Frontend → auth-server로 리다이렉트 (/auth/gmail/login)
|
||||||
|
↓
|
||||||
|
2. Auth-server → Google OAuth → 콜백 받음
|
||||||
|
↓
|
||||||
|
3. Auth-server → Redis에 임시 코드 저장 → Frontend로 리다이렉트 (#code=xxx)
|
||||||
|
↓
|
||||||
|
4. Frontend → 코드로 토큰 교환 (/auth/verify) → JWT 토큰 받음
|
||||||
|
↓
|
||||||
|
5. Frontend → localStorage에 토큰 저장
|
||||||
|
↓
|
||||||
|
6. Frontend → robing-gateway 호출 시 X-User-Id 헤더만 전송 ⚠️
|
||||||
|
↓
|
||||||
|
7. robing-gateway → 토큰 검증 없이 X-User-Id 신뢰 ⚠️
|
||||||
|
```
|
||||||
|
|
||||||
|
## 발견된 보안 취약점
|
||||||
|
|
||||||
|
### 1. 인증 토큰 미전달
|
||||||
|
- **현상**: Frontend가 JWT 토큰을 저장하지만 API 호출 시 전송하지 않음
|
||||||
|
- **코드 위치**: frontend의 API 클라이언트
|
||||||
|
- **영향**: 인증 메커니즘이 무의미함
|
||||||
|
|
||||||
|
### 2. 토큰 검증 부재
|
||||||
|
- **현상**: robing-gateway가 X-User-Id 헤더만 확인하고 JWT 검증 없음
|
||||||
|
- **코드 예시**:
|
||||||
|
```python
|
||||||
|
# 현재 (취약함)
|
||||||
|
user_id = request.headers.get("X-User-Id", "default")
|
||||||
|
|
||||||
|
# 필요한 것
|
||||||
|
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||||
|
user_id = verify_jwt_token(token) # 검증 필요!
|
||||||
|
```
|
||||||
|
- **영향**: 누구나 헤더 조작으로 다른 사용자 행세 가능
|
||||||
|
|
||||||
|
### 3. 시스템 간 연결 끊김
|
||||||
|
- **auth-server**: JWT 발급 기능 있음 (51123 서버, 포트 9000)
|
||||||
|
- **robing-gateway**: JWT 검증 기능 없음 (51124 서버)
|
||||||
|
- **frontend**: 토큰 보유하지만 전송 안 함
|
||||||
|
- **영향**: 인증 시스템이 형식적으로만 존재
|
||||||
|
|
||||||
|
### 4. 기본값 폴백 문제
|
||||||
|
- **현상**: X-User-Id 없으면 "default"로 처리
|
||||||
|
- **영향**: 모든 미인증 사용자가 동일한 로빙/데이터 공유
|
||||||
|
|
||||||
|
## 긴급 수정 필요사항
|
||||||
|
|
||||||
|
### Frontend 수정
|
||||||
|
```javascript
|
||||||
|
// API 클라이언트에 토큰 헤더 추가
|
||||||
|
const apiCall = async (endpoint, data) => {
|
||||||
|
const token = localStorage.getItem('auth_token');
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': token ? `Bearer ${token}` : '' // 추가!
|
||||||
|
};
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### robing-gateway 수정
|
||||||
|
```python
|
||||||
|
# JWT 검증 미들웨어 추가
|
||||||
|
from jose import jwt, JWTError
|
||||||
|
|
||||||
|
async def verify_token(request: Request):
|
||||||
|
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||||
|
if not token:
|
||||||
|
raise HTTPException(status_code=401, detail="Token missing")
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
|
||||||
|
request.state.user_id = payload.get("sub", "default")
|
||||||
|
except JWTError:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid token")
|
||||||
|
|
||||||
|
return request.state.user_id
|
||||||
|
|
||||||
|
@app.post("/api/chat")
|
||||||
|
async def chat(request: Request):
|
||||||
|
user_id = await verify_token(request) # 검증 필수!
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 시스템 간 연동
|
||||||
|
1. **JWT 시크릿 공유**: auth-server와 robing-gateway가 동일한 JWT_SECRET 사용
|
||||||
|
2. **환경변수 설정**:
|
||||||
|
```bash
|
||||||
|
# .env 파일
|
||||||
|
JWT_SECRET=동일한_시크릿_키_사용
|
||||||
|
JWT_ALGORITHM=HS256
|
||||||
|
JWT_EXPIRY_HOURS=24
|
||||||
|
```
|
||||||
|
|
||||||
|
### 에러 처리
|
||||||
|
```python
|
||||||
|
# 인증 실패 시 명확한 에러
|
||||||
|
@app.exception_handler(401)
|
||||||
|
async def unauthorized_handler(request, exc):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=401,
|
||||||
|
content={"error": "Authentication required", "detail": str(exc)}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 추가 개선사항
|
||||||
|
|
||||||
|
### 1. Refresh Token 구현
|
||||||
|
```python
|
||||||
|
# 장기 세션 유지를 위한 refresh token
|
||||||
|
def create_tokens(user_id: str):
|
||||||
|
access_token = create_jwt(user_id, expires_in=timedelta(hours=1))
|
||||||
|
refresh_token = create_jwt(user_id, expires_in=timedelta(days=30))
|
||||||
|
return access_token, refresh_token
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 토큰 블랙리스트
|
||||||
|
```python
|
||||||
|
# Redis를 활용한 로그아웃 토큰 관리
|
||||||
|
async def logout(token: str):
|
||||||
|
await redis.setex(f"blacklist:{token}", JWT_EXPIRY_HOURS * 3600, "1")
|
||||||
|
|
||||||
|
async def is_token_blacklisted(token: str):
|
||||||
|
return await redis.exists(f"blacklist:{token}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Role-Based Access Control (RBAC)
|
||||||
|
```python
|
||||||
|
# JWT에 역할 정보 포함
|
||||||
|
payload = {
|
||||||
|
"sub": user_id,
|
||||||
|
"email": user_email,
|
||||||
|
"role": "admin", # or "user", "guest"
|
||||||
|
"permissions": ["read", "write", "delete"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 서버별 현황
|
||||||
|
|
||||||
|
### 51123 서버
|
||||||
|
- **auth-server** 실행 중 (포트 9000)
|
||||||
|
- JWT 발급 기능 정상
|
||||||
|
- Redis 임시 코드 시스템 작동
|
||||||
|
|
||||||
|
### 51124 서버
|
||||||
|
- **robing-gateway** 실행 중
|
||||||
|
- JWT 검증 로직 없음 ⚠️
|
||||||
|
- 모든 로빙 서비스 운영
|
||||||
|
|
||||||
|
### 로컬 개발
|
||||||
|
- **frontend** 개발
|
||||||
|
- 토큰 저장은 하지만 전송 안 함 ⚠️
|
||||||
|
|
||||||
|
## 보안 위험 수준
|
||||||
|
|
||||||
|
**🔴 Critical (긴급)**
|
||||||
|
|
||||||
|
현재 상태에서는:
|
||||||
|
1. 누구나 다른 사용자로 위장 가능
|
||||||
|
2. 개인 데이터 접근 가능
|
||||||
|
3. 로빙 설정 변경 가능
|
||||||
|
4. 실질적인 인증 보호 없음
|
||||||
|
|
||||||
|
## 구현 우선순위
|
||||||
|
|
||||||
|
1. **즉시 (1일 내)**
|
||||||
|
- robing-gateway에 JWT 검증 추가
|
||||||
|
- Frontend API 호출 시 토큰 헤더 추가
|
||||||
|
|
||||||
|
2. **긴급 (1주 내)**
|
||||||
|
- JWT 시크릿 환경변수 통일
|
||||||
|
- 에러 처리 및 로깅 추가
|
||||||
|
|
||||||
|
3. **중요 (2주 내)**
|
||||||
|
- Refresh token 구현
|
||||||
|
- 토큰 블랙리스트 관리
|
||||||
|
- RBAC 권한 시스템
|
||||||
|
|
||||||
|
## 테스트 체크리스트
|
||||||
|
|
||||||
|
- [ ] Frontend에서 Authorization 헤더 전송 확인
|
||||||
|
- [ ] robing-gateway JWT 검증 동작 확인
|
||||||
|
- [ ] 잘못된 토큰 시 401 에러 반환
|
||||||
|
- [ ] 토큰 없을 시 적절한 에러 메시지
|
||||||
|
- [ ] 토큰 만료 시 재로그인 유도
|
||||||
|
- [ ] 로그아웃 시 토큰 무효화
|
||||||
|
|
||||||
|
## 참고사항
|
||||||
|
|
||||||
|
- JWT 라이브러리: python-jose (이미 사용 중)
|
||||||
|
- Redis: 임시 코드 및 블랙리스트 관리
|
||||||
|
- 환경변수: JWT_SECRET 반드시 동일하게 설정
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**"인증은 시스템의 문지기입니다. 문이 열려있다면 의미가 없습니다."**
|
||||||
|
|
||||||
|
이 문제는 즉시 수정이 필요한 보안 취약점입니다.
|
||||||
Loading…
x
Reference in New Issue
Block a user