feat(security): API 키 보호, CORS 강화, Rate Limiting KV 전환

보안 개선:
- API 키 하드코딩 제거 (NAMECHEAP_API_KEY_INTERNAL)
- CORS 정책: * → hosting.anvil.it.com 제한
- /health 엔드포인트 DB 정보 노출 방지
- Rate Limiting 인메모리 Map → Cloudflare KV 전환
  - 분산 환경 일관성 보장
  - 재시작 후에도 유지
  - 자동 만료 (TTL)

문서:
- CLAUDE.md Security 섹션 추가
- KV Namespace 설정 가이드 추가
- 배포/마이그레이션 가이드 추가

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-19 15:20:14 +09:00
parent 6d4fd7f22f
commit 4eb5bbd3d3
11 changed files with 1277 additions and 66 deletions

265
DEPLOYMENT_SUMMARY.md Normal file
View File

@@ -0,0 +1,265 @@
# Rate Limiting KV Migration - 배포 요약
## 변경 사항
### 1. Rate Limiting 시스템 마이그레이션
- **이전:** 인메모리 Map (Worker 인스턴스별 독립)
- **현재:** Cloudflare KV (분산 환경 공유)
### 2. 해결된 문제
✅ Workers 인스턴스 간 Rate Limit 데이터 공유
✅ Worker 재시작 시에도 Rate Limit 상태 유지
✅ 분산 환경에서 일관된 Rate Limiting 동작
✅ 자동 만료 (KV TTL) - 메모리 효율성
---
## 수정된 파일
### 1. `/Users/kaffa/telegram-bot-workers/wrangler.toml`
```toml
[[kv_namespaces]]
binding = "RATE_LIMIT_KV"
id = "YOUR_KV_NAMESPACE_ID" # 생성 후 실제 ID로 교체 필요
```
### 2. `/Users/kaffa/telegram-bot-workers/src/types.ts`
```typescript
export interface Env {
// ... 기존 필드들
RATE_LIMIT_KV: KVNamespace; // ✅ 추가
}
```
### 3. `/Users/kaffa/telegram-bot-workers/src/security.ts`
- 인메모리 Map 제거 (`rateLimitMap`)
- `cleanupRateLimits()` 함수 제거 (KV TTL로 자동 관리)
- `checkRateLimit()` 함수 시그니처 변경:
- 이전: `checkRateLimit(userId: string): boolean`
- 현재: `checkRateLimit(kv: KVNamespace, userId: string): Promise<boolean>`
### 4. `/Users/kaffa/telegram-bot-workers/src/index.ts`
```typescript
// 이전
if (!checkRateLimit(telegramUserId)) {
// ...
}
// 현재
if (!(await checkRateLimit(env.RATE_LIMIT_KV, telegramUserId))) {
// ...
}
```
### 5. `/Users/kaffa/telegram-bot-workers/CLAUDE.md`
- Rate Limiting 섹션 업데이트 (KV 기반 설명 추가)
- Configuration 섹션에 KV Namespaces 테이블 추가
- Commands 섹션에 KV Namespace 생성 명령 추가
---
## 배포 전 필수 작업
### Step 1: KV Namespace 생성
```bash
wrangler kv:namespace create RATE_LIMIT_KV
```
**출력 예시:**
```
⛅️ wrangler 3.x.x
-------------------
🌀 Creating namespace with title "telegram-summary-bot-RATE_LIMIT_KV"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "RATE_LIMIT_KV", id = "abc123..." }
```
### Step 2: wrangler.toml 수정
출력된 `id` 값을 복사하여 `wrangler.toml` 22번 줄 수정:
```toml
id = "abc123def456ghi789jkl012mno345pq" # ← 실제 ID로 변경
```
### Step 3: TypeScript 컴파일 확인
```bash
npx tsc --noEmit
```
### Step 4: 로컬 테스트
```bash
npm run dev
```
다른 터미널에서:
```bash
curl -X POST http://localhost:8787/webhook \
-H "Content-Type: application/json" \
-H "X-Telegram-Bot-Api-Secret-Token: test-secret" \
-d '{"update_id":1,"message":{"message_id":1,"from":{"id":123,"is_bot":false,"first_name":"Test"},"chat":{"id":123,"type":"private"},"date":1234567890,"text":"테스트"}}'
```
### Step 5: Production 배포
```bash
npm run deploy
```
### Step 6: 배포 확인
```bash
# Health Check
curl https://telegram-summary-bot.kappa-d8e.workers.dev/health
# 로그 스트리밍
npm run tail
```
---
## 기술 상세
### Rate Limiting 동작 방식
**Key 형식:** `ratelimit:{userId}`
**데이터 구조:**
```typescript
{
count: number, // 현재 윈도우 내 요청 수
resetAt: number // 윈도우 만료 시각 (Unix timestamp)
}
```
**알고리즘:**
1. KV에서 `ratelimit:{userId}` 조회
2. 데이터 없음 또는 윈도우 만료 (`now > resetAt`)
→ 새 윈도우 시작 (`count=1`, `resetAt=now+60000`)
3. `count >= 30` (기본값)
→ Rate Limit 초과, 요청 차단
4. `count < 30`
`count++` 후 KV 업데이트
**자동 만료:**
- KV의 `expirationTtl` 옵션 사용
- 윈도우 종료 시 자동 삭제 (메모리 효율)
**에러 처리:**
- KV 오류 발생 시: Rate Limit 통과 (서비스 가용성 우선)
- 로그 기록: `[RateLimit] KV 오류: ...`
---
## 모니터링
### KV Dashboard
https://dash.cloudflare.com → Workers & Pages → KV → telegram-summary-bot-RATE_LIMIT_KV
### KV CLI 명령어
```bash
# Namespace 목록
wrangler kv:namespace list
# 특정 키 조회
wrangler kv:key get "ratelimit:821596605" --namespace-id=YOUR_KV_ID
# 모든 키 목록
wrangler kv:key list --namespace-id=YOUR_KV_ID
# 키 삭제 (테스트용)
wrangler kv:key delete "ratelimit:821596605" --namespace-id=YOUR_KV_ID
```
### 로그 확인
```bash
# 실시간 로그
wrangler tail
# Rate Limit 관련 로그만 필터링
wrangler tail --format json | jq 'select(.message | contains("RateLimit"))'
```
---
## 성능 영향
### Before (인메모리 Map)
- 응답 시간: ~1ms (동기)
- 메모리: 인스턴스별 독립 (중복 저장)
- 일관성: ❌ 분산 환경에서 불일치
### After (KV)
- 응답 시간: ~20-50ms (KV read/write 포함)
- 메모리: 0 (KV로 오프로드)
- 일관성: ✅ 전역 일관성 보장
**영향 분석:**
- 약 20-50ms 지연 추가 (허용 가능한 수준)
- Telegram Webhook 응답은 200ms 이내 권장 (충분히 만족)
- 사용자 경험에 무시할 수 있는 영향
---
## 비용 분석
### Cloudflare Workers Free Plan
- **KV 읽기:** 100,000 reads/day (무료)
- **KV 쓰기:** 1,000 writes/day (무료)
### Rate Limiting 사용량
- 1 메시지 = 1 read + 1 write (최악의 경우)
- 일일 1,000 메시지까지 무료 (write limit 기준)
- 초과 시: $0.50 per million writes
**예상 사용량:**
- 현재 사용자 수: 소규모 (~10명)
- 일일 메시지 수: ~100개
- 예상 비용: $0 (무료 한도 내)
---
## 롤백 절차 (문제 발생 시)
### Option 1: Git Revert
```bash
git revert HEAD
npm run deploy
```
### Option 2: 수동 롤백
1. `wrangler.toml`에서 KV Namespace 제거
2. `src/types.ts`에서 `RATE_LIMIT_KV` 제거
3. `src/security.ts` 이전 버전으로 복원
4. `src/index.ts` 이전 버전으로 복원
5. `npm run deploy`
---
## 추가 리소스
- **상세 마이그레이션 가이드:** `/Users/kaffa/telegram-bot-workers/KV_MIGRATION_GUIDE.md`
- **Cloudflare KV Docs:** https://developers.cloudflare.com/kv/
- **Workers KV Limits:** https://developers.cloudflare.com/workers/platform/limits/#kv
---
## 체크리스트
배포 전:
- [ ] `wrangler kv:namespace create RATE_LIMIT_KV` 실행
- [ ] `wrangler.toml`에 실제 KV Namespace ID 입력
- [ ] `npx tsc --noEmit` 타입 에러 없음
- [ ] `npm run dev` 로컬 테스트 성공
- [ ] Rate Limit 테스트 (30회 연속 요청)
배포 후:
- [ ] `npm run deploy` 성공
- [ ] Health Check 정상 (`/health` 엔드포인트)
- [ ] 실제 Telegram 메시지 테스트 성공
- [ ] KV Dashboard에서 키 생성 확인
- [ ] `wrangler tail` 로그 확인
- [ ] 30회 연속 메시지로 Rate Limit 동작 확인
---
**작업 완료 시각:** 2026-01-19
**배포 담당자:** Claude Code (AI Assistant)
**상태:** ✅ 코드 변경 완료 / ⏳ KV Namespace 생성 대기 중