API Routing Conventions
새 endpoint / secret 추가 시 따를 컨벤션. PR #79 (Registry 도입, 2026-05-12) + 봇 리뷰 라운드에서 합의된 룰을 영구화.
Why
ACL Registry 가 helper 자동 선택을 강제하지만 path 선택 / secret 비교 방식 까지는 자동화 X. 명시 안 하면 새 endpoint 추가하는 사람이 옛 패턴 (path-prefix exempt 매트릭스 / 일반 == 비교) 으로 회귀할 위험.
룰
R1 — Audience-based path
endpoint 가 호출되는 청중에 따라 path prefix 결정:
| 청중 | path | 등록 helper |
|---|---|---|
| admin (X-Admin-Key) 전용 | /api/v1/admin/* | Admin |
| 사용자 (모바일/웹) 전용 | /api/v1/* (admin 제외) | Authenticated / Anonymous / AuthIssue |
| 시스템 (Google SSV, health probe) | /api/v1/admob/ssv, /actuator/* | AdMobSSV / Actuator |
한 동작을 두 청중이 모두 호출 하면 endpoint 2 개 (각 청중의 path 아래). 같은 핸들러를 두 helper 로 wrap 하지 말고, 핸들러 로직을 서비스 함수로 빼고 각 endpoint 핸들러가 서비스 호출.
예: PR #79 에서 POST /api/v1/disclosures/fetch (JWT 보호 — admin-only 디버깅 endpoint 임) → POST /api/v1/admin/disclosures/fetch 로 이동. 사용자가 호출할 일 없는 endpoint 가 admin path 밖에 있으면 audience contract 가 깨짐.
R2 — Registry helper 강제
mux.Handle / mux.HandleFunc 직접 호출 금지. 모든 라우트는 pkg/route.Registry 의 6 helper 중 하나로 등록:
Authenticated— AppCheck + JWT 필수Anonymous— AppCheck (or Worker secret) + JWT 선택. 비로그인 read.AuthIssue— AppCheck + JWT 면제. 토큰 발급 자체.Admin— X-Admin-KeyAdMobSSV— ECDSA (핸들러 안)Actuator— 의도된 무 ACL
Registry 우회 (mux 직접 등록) 시 ACL 누락 가능. 카테고리 매트릭스는 docs/spec/backend-structure.md "Route ACL 매트릭스" 참조.
R3 — Secret 비교는 ConstantTimeCompare
X-Admin-Key, X-Worker-Secret 등 secret 비교는 crypto/subtle.ConstantTimeCompare 사용. 일반 == / != 는 첫 바이트 불일치에서 short-circuit → 타이밍 oracle 로 prefix 노출 가능.
import "crypto/subtle"
if subtle.ConstantTimeCompare([]byte(got), []byte(expected)) != 1 {
w.WriteHeader(http.StatusForbidden)
return
}HTTPS + IP rate limit 으로 실제 exploit 난이도는 높지만, secret 비교는 cost-free 한 줄 교체이므로 일관 적용.
R4 — RateLimit 우선순위
Authenticated / Admin chain 에서 rate limit 미들웨어가 인증 검증 (RequireAuth / RequireAdminKey) 보다 바깥:
// 올바름
appCheckMW(generalRLMW(requireAuthMW(next)))
generalRLMW(requireAdminKeyMW(next))
// 잘못됨 — 인증 실패 요청이 RL 토큰 소비 없이 차단
appCheckMW(requireAuthMW(generalRLMW(next)))
requireAdminKeyMW(generalRLMW(next))인증 검증이 바깥이면 무인증/잘못된 키 요청은 RL 소비 없이 즉시 401/403 → 방어 심도 손실 (브루트포스 시도가 RL 카운터에 안 잡힘, soft AppCheck 모드에선 무인증 플러딩 가능).
적용 시점
- 새 endpoint 핸들러 작성 시 → R1, R2 검토
- 새 secret 환경변수 추가 + 검증 미들웨어 작성 시 → R3 적용
- Registry helper 추가 (7번째 카테고리 신설) 시 → R4 패턴 따라 chain 합성
참조
- 구현:
backend/pkg/route/registry.go,backend/pkg/middleware/admin_key.go,backend/pkg/middleware/worker_context.go - 매트릭스:
docs/spec/backend-structure.md"Route ACL 매트릭스" - PR #79 (Registry 도입), 봇 리뷰 P1/P2 코멘트 6건