fix: 아키텍처 리뷰 HIGH/MEDIUM 이슈 10건 수정
All checks were successful
Server CI/CD / lint-and-build (push) Successful in 34s
Server CI/CD / deploy (push) Successful in 50s

HIGH (3건):
- 런처 파일 업로드 시 PE 헤더 검증 + 500MB 크기 제한 추가
- 체인 노드 URL 파싱 시 scheme/host 유효성 검증
- Dockerfile 비루트 사용자(app:1000) 실행

MEDIUM (7건):
- SSAFY username 충돌 시 랜덤 suffix로 최대 3회 재시도
- 내부 API username 검증 validID(256자) → validUsername(3~50자) 분리
- 동시 업로드 경합 방지 sync.Mutex 추가
- 프로덕션 환경변수 검증 강화 (DB_PASSWORD, OPERATOR_KEY_HEX, INTERNAL_API_KEY)
- Redis 에러 시 멱등성 요청 통과 → 503 거부로 변경
- CORS AllowOrigins 환경변수화 (CORS_ALLOW_ORIGINS)
- Refresh 엔드포인트 rate limiting 추가 (IP당 5 req/min)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 15:56:58 +09:00
parent e16e1b5e0a
commit cc9884bdfe
9 changed files with 140 additions and 47 deletions

View File

@@ -2,6 +2,7 @@ package config
import (
"log"
"net/url"
"os"
"strconv"
"strings"
@@ -35,6 +36,9 @@ type Config struct {
OperatorKeyHex string
WalletEncryptionKey string
// CORS
CORSAllowOrigins string
// Server-to-server auth
InternalAPIKey string
@@ -72,6 +76,8 @@ func Load() {
OperatorKeyHex: getEnv("OPERATOR_KEY_HEX", ""),
WalletEncryptionKey: getEnv("WALLET_ENCRYPTION_KEY", ""),
CORSAllowOrigins: getEnv("CORS_ALLOW_ORIGINS", "https://a301.tolelom.xyz"),
InternalAPIKey: getEnv("INTERNAL_API_KEY", ""),
SSAFYClientID: getEnv("SSAFY_CLIENT_ID", ""),
@@ -83,6 +89,9 @@ func Load() {
if raw := getEnv("CHAIN_NODE_URLS", ""); raw != "" {
for _, u := range strings.Split(raw, ",") {
if u = strings.TrimSpace(u); u != "" {
if parsed, err := url.Parse(u); err != nil || parsed.Scheme == "" || parsed.Host == "" {
log.Fatalf("FATAL: invalid CHAIN_NODE_URL: %q (must be http:// or https://)", u)
}
C.ChainNodeURLs = append(C.ChainNodeURLs, u)
}
}
@@ -114,8 +123,23 @@ func WarnInsecureDefaults() {
log.Println("WARNING: WALLET_ENCRYPTION_KEY is empty — blockchain wallet features will fail")
}
if isProd {
if C.DBPassword == "" {
log.Println("FATAL: DB_PASSWORD must be set in production")
insecure = true
}
if C.OperatorKeyHex == "" {
log.Println("FATAL: OPERATOR_KEY_HEX must be set in production")
insecure = true
}
if C.InternalAPIKey == "" {
log.Println("FATAL: INTERNAL_API_KEY must be set in production")
insecure = true
}
}
if isProd && insecure {
log.Fatal("FATAL: insecure default secrets detected in production — set JWT_SECRET, REFRESH_SECRET, and ADMIN_PASSWORD")
log.Fatal("FATAL: insecure defaults detected in production — check warnings above")
}
}