fix: 보안 강화 및 안정성 개선
All checks were successful
Server CI/CD / deploy (push) Successful in 5s

- fileHash 빈 문자열 시 게임 업로드 거부 (A301.exe 누락 zip 차단)
- Rate limiting 추가: 인증 API 10req/min, 일반 API 60req/min
- 블록체인 트랜잭션 Idempotency-Key 미들웨어 (Redis 캐싱, 10분 TTL)
- 파일 업로드 크기 제한 4GB (BodyLimit)
- Username 대소문자 정규화 (Register/Login에서 소문자 변환)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 11:10:23 +09:00
parent 4843470310
commit 26876ba8ca
7 changed files with 123 additions and 21 deletions

View File

@@ -15,14 +15,16 @@ func Register(
annH *announcement.Handler,
dlH *download.Handler,
chainH *chain.Handler,
authLimiter fiber.Handler,
apiLimiter fiber.Handler,
) {
api := app.Group("/api")
api := app.Group("/api", apiLimiter)
// Auth
a := api.Group("/auth")
a.Post("/register", authH.Register)
a.Post("/login", authH.Login)
a.Post("/refresh", authH.Refresh)
a.Post("/register", authLimiter, authH.Register)
a.Post("/login", authLimiter, authH.Login)
a.Post("/refresh", authLimiter, authH.Refresh)
a.Post("/logout", middleware.Auth, authH.Logout)
a.Post("/verify", authH.VerifyToken)
@@ -57,25 +59,25 @@ func Register(
ch.Get("/market", chainH.GetMarketListings)
ch.Get("/market/:id", chainH.GetMarketListing)
// Chain - User Transactions (authenticated)
ch.Post("/transfer", chainH.Transfer)
ch.Post("/asset/transfer", chainH.TransferAsset)
ch.Post("/market/list", chainH.ListOnMarket)
ch.Post("/market/buy", chainH.BuyFromMarket)
ch.Post("/market/cancel", chainH.CancelListing)
ch.Post("/inventory/equip", chainH.EquipItem)
ch.Post("/inventory/unequip", chainH.UnequipItem)
// Chain - User Transactions (authenticated, idempotency-protected)
ch.Post("/transfer", middleware.Idempotency, chainH.Transfer)
ch.Post("/asset/transfer", middleware.Idempotency, chainH.TransferAsset)
ch.Post("/market/list", middleware.Idempotency, chainH.ListOnMarket)
ch.Post("/market/buy", middleware.Idempotency, chainH.BuyFromMarket)
ch.Post("/market/cancel", middleware.Idempotency, chainH.CancelListing)
ch.Post("/inventory/equip", middleware.Idempotency, chainH.EquipItem)
ch.Post("/inventory/unequip", middleware.Idempotency, chainH.UnequipItem)
// Chain - Admin Transactions (admin only)
// Chain - Admin Transactions (admin only, idempotency-protected)
chainAdmin := api.Group("/chain/admin", middleware.Auth, middleware.AdminOnly)
chainAdmin.Post("/mint", chainH.MintAsset)
chainAdmin.Post("/reward", chainH.GrantReward)
chainAdmin.Post("/template", chainH.RegisterTemplate)
chainAdmin.Post("/mint", middleware.Idempotency, chainH.MintAsset)
chainAdmin.Post("/reward", middleware.Idempotency, chainH.GrantReward)
chainAdmin.Post("/template", middleware.Idempotency, chainH.RegisterTemplate)
// Internal - Game server endpoints (API key auth, username-based)
// Internal - Game server endpoints (API key auth, username-based, idempotency-protected)
internal := api.Group("/internal/chain", middleware.ServerAuth)
internal.Post("/reward", chainH.InternalGrantReward)
internal.Post("/mint", chainH.InternalMintAsset)
internal.Post("/reward", middleware.Idempotency, chainH.InternalGrantReward)
internal.Post("/mint", middleware.Idempotency, chainH.InternalMintAsset)
internal.Get("/balance", chainH.InternalGetBalance)
internal.Get("/assets", chainH.InternalGetAssets)
internal.Get("/inventory", chainH.InternalGetInventory)