fix: 코드 리뷰 기반 보안·안정성 개선 (14건)
All checks were successful
Server CI/CD / deploy (push) Successful in 1m36s

- unsafe 타입 단언 → safe assertion (chain handler 11곳, auth Logout)
- Repository 에러 시 nil 반환으로 통일 (chain, auth, announcement)
- string ID → uint 파싱으로 타입 안전성 확보 (auth, announcement)
- CORS AllowHeaders에 Idempotency-Key, X-API-Key 추가
- /verify 엔드포인트 rate limiter 적용
- Redis 호출에 context timeout 적용 (auth, idempotency 미들웨어)
- chain handler 에러 응답에서 내부 정보 노출 방지
- f.Close() 에러 검사 추가 (download service 2곳)
- 공지사항 Delete 404 응답 추가
- 회원가입 롤백 시 Delete 에러 로깅

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 14:43:19 +09:00
parent 3d0c9e5670
commit 23bec776ab
14 changed files with 274 additions and 89 deletions

View File

@@ -1,5 +1,7 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
@@ -10,47 +12,102 @@ docker build -t a301-server . # Docker 빌드
## Tech Stack
- **Go** + **Fiber v2** (StreamRequestBody: true — 대용량 업로드용)
- **Go** + **Fiber v2** (StreamRequestBody: true — 대용량 업로드용, Body Limit 4GB)
- **GORM** + **MySQL** (AutoMigrate 사용)
- **Redis** — JWT 블랙리스트
- **JWT** — `golang-jwt/jwt v5`
- **Redis** — JWT 세션 저장(`session:{userID}`, `refresh:{userID}`) + 멱등성 캐시
- **JWT** — `golang-jwt/jwt v5`, Access + Refresh 토큰 로테이션
## Project Purpose
"One of the plans" 게임 플랫폼 백엔드.
인증 / 공지사항 / 게임 파일(game.zip + launcher.exe) 업로드·서빙 담당.
인증 / 공지사항 / 게임 파일 업로드·서빙 / 블록체인(TOL Chain) 연동 담당.
## Project Structure
```
internal/
├── auth/ # User 모델, JWT 발급·검증, 블랙리스트(Redis)
├── auth/ # User 모델, JWT 발급·검증, 세션 관리(Redis)
├── announcement/ # Announcement CRUD
── download/ # Info 조회, 파일 업로드(스트리밍), 파일 서빙
── download/ # Info 조회, 파일 업로드(스트리밍), 파일 서빙
└── chain/ # 블록체인 지갑·거래·마켓·인벤토리 (5파일: handler, service, repository, model, client)
pkg/
├── config/ # 환경변수 → Config 구조체
├── database/ # ConnectMySQL(), ConnectRedis()
└── middleware/ # Auth (JWT 검증), AdminOnly
└── middleware/ # Auth, AdminOnly, ServerAuth, Idempotency
routes/routes.go # 모든 라우트 등록
```
## Key Patterns
- **계층 구조**: `Handler → Service → Repository`. 각 도메인 폴더에 4파일(handler, service, repository, model).
- **계층 구조**: `Handler → Service → Repository`. 각 도메인 폴더에 4~5파일.
- **파일 업로드**: `Fiber StreamRequestBody: true` + `io.Copy`로 raw body를 직접 디스크에 스트리밍. 메모리에 파일 올리지 않음.
- **SHA256 자동 추출**: 게임 zip 업로드 시 zip 내 `A301.exe`를 스트리밍으로 읽어 해시 계산.
- **CORS**: `AllowMethods``PATCH` 포함 필수 (유저 권한 변경 엔드포인트).
- **CORS**: `AllowMethods``PATCH` 포함 필수 (유저 권한 변경 엔드포인트). `AllowOrigins`: `https://a301.tolelom.xyz`.
- **초기 admin 계정**: 서버 시작 시 `EnsureAdmin()`으로 존재 확인 후 없으면 생성.
- **DI 패턴**: `main.go`에서 생성자 함수로 Repo → Service → Handler 주입.
- **콜백 연결**: `authSvc.SetWalletCreator()` — 회원가입 시 자동 지갑 생성. `chainSvc.SetUserResolver()` — username → userID 변환.
## Middleware
- **Auth**: `Authorization: Bearer <jwt>` 검증 + Redis 세션 확인. `c.Locals("userID", "username", "role")` 설정.
- **AdminOnly**: `role == "admin"` 확인, 아니면 403.
- **ServerAuth**: `X-API-Key` 헤더 검증. 게임 서버 → API 서버 내부 통신용.
- **Idempotency**: `Idempotency-Key` 헤더로 중복 요청 방지. Redis 캐시 TTL 10분. 블록체인 트랜잭션 이중 지출 방지용.
## JWT 토큰 로테이션
- **Access Token**: `JWT_SECRET`으로 서명, 기본 24시간 만료, Redis `session:{userID}`에 저장.
- **Refresh Token**: `REFRESH_SECRET`으로 서명, 7일 만료, Redis `refresh:{userID}`에 저장.
- **Refresh 시**: 이전 토큰 무효화 + 새 Access/Refresh 쌍 발급 (로테이션).
- **Logout**: Redis에서 session + refresh 키 모두 삭제.
## Rate Limiting
- Auth 엔드포인트: IP당 10 req/min
- 일반 API: IP당 60 req/min
## 블록체인 연동 (internal/chain/)
TOL Chain 노드와 JSON-RPC 2.0 통신.
- **UserWallet 모델**: ed25519 키페어 생성, 개인키는 AES-256-GCM 암호화 후 DB 저장.
- **client.go**: Chain 노드 RPC 호출 (10초 타임아웃).
- **service.go**: 지갑 생성/암호화, 트랜잭션 서명, 마켓/인벤토리 로직.
- **내부 API**: 게임 서버가 username 기반으로 보상 지급 (`/api/internal/chain/*`).
## Routes
**인증**: `POST /api/auth/{register,login,refresh,logout,verify}` (register/login/refresh는 rate limit)
**유저 관리 (admin)**: `GET /api/users/`, `PATCH /api/users/:id/role`, `DELETE /api/users/:id`
**공지사항**: `GET /api/announcements/`, `POST|PUT|DELETE /api/announcements/:id` (CUD는 admin)
**다운로드**: `GET /api/download/{info,file,launcher}`, `POST /api/download/upload/{game,launcher}` (upload는 admin)
**체인 조회 (JWT)**: `GET /api/chain/{wallet,balance,assets,asset/:id,inventory,market,market/:id}`
**체인 트랜잭션 (JWT + Idempotency)**: `POST /api/chain/{transfer,asset/transfer,market/list,market/buy,market/cancel,inventory/equip,inventory/unequip}`
**체인 관리자 (JWT + Admin + Idempotency)**: `POST /api/chain/admin/{mint,reward,template}`
**내부 API (X-API-Key + Idempotency)**: `POST /api/internal/chain/{reward,mint}`, `GET /api/internal/chain/{balance,assets,inventory}` (username 쿼리 파라미터)
## Environment Variables
필수 운영 설정:
```
APP_PORT=8080
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME
REDIS_ADDR, REDIS_PASSWORD
JWT_SECRET, REFRESH_SECRET, JWT_EXPIRY_HOURS(기본24)
ADMIN_USERNAME, ADMIN_PASSWORD
BASE_URL=https://a301.api.tolelom.xyz
GAME_DIR=/data/game
JWT_SECRET=<강력한 랜덤값>
ADMIN_USERNAME=admin
ADMIN_PASSWORD=<강력한 비밀번호>
CHAIN_NODE_URL=http://localhost:8545
CHAIN_ID=tolchain-dev
OPERATOR_KEY_HEX # 오퍼레이터 개인키 (블록체인 트랜잭션 서명용)
WALLET_ENCRYPTION_KEY # 64자 hex = 32바이트 AES-256 키 (지갑 암호화)
INTERNAL_API_KEY # 게임 서버 인증용 API 키
```
## File Storage