애플리케이션 보안

API 보안

meellon 2026. 7. 1. 06:00

학습 목표

REST/JSON API에서 인증·인가 다음에 필요한 운영·경계 통제를 설명할 수 있다.

rate limiting 알고리즘·키 단위·429 응답 설계를 설명할 수 있다.

스키마 검증으로 Injection·mass assignment·타입 혼동경계에서 차단하는 이유를 설명할 수 있다.

CORS브라우저 정책임을 설명하고 preflight·credentials 오설정 위험을 설명할 수 있다.

API 게이트웨이··WAF에서 defense in depth 역할을 구분할 수 있다.

문제 상황

  • 공개 API 키 없이 호출 — 스크래퍼가 분당 수천 요청·DB 고갈
  • POST /users{"role":"admin"} 추가 — mass assignment로 권한 상승 (11편)
  • page=999999&size=1000000OOM·타임아웃 (DoS)
  • Origin: https://evil.com인데 Access-Control-Allow-Origin: * + 쿠키 — 브라우저 정책 오해
  • Content-Type: text/plain으로 JSON 파서 우회 시도
  • 모바일·서버는 CORS 무관 — “CORS 막았으니 안전” 착각
  • 13편 API 키를 헤더에 넣었지만 rate limit 없음 — 유출 시 무제한 악용

Part 4 운영 보안 — 시크릿 다음은 API 경계에서 남용·잘못된 입력·브라우저 정책을 다룬다.

본 글 예제는 방어 설계 목적이다. 허가 없는 API 남용·침투는 불법이다.

1. API 위협 표면

API — 기계 간 계약. 웹 UI보다 자동화·병렬 호출이 쉬워 남용·스캔이 빈번하다.

계층 통제  
Edge WAF, DDoS, TLS Network·Platform
Gateway rate limit, API key, mTLS Kong, ALB, Ingress
AuthN/Z JWT, OAuth, RBAC 5~8편
App schema validation, business rule 본편
Data prepared statement, least privilege 9편
클라이언트 → (TLS) → gateway rate limit → auth → validation → handler → DB

OWASP API Security Top 10 — BOLA(객체 권한), unrestricted resource consumption, unsafe consumption of APIs 등 — 본편은 소비 제한·입력·CORS에 초점.

2. Rate limiting

목적가용성(DoS·비용)·공정 사용·유출 키 blast radius 축소.

알고리즘 동작  
Fixed window 분당 N회 카운터 경계에서 burst 2N 가능
Sliding window 최근 T초 이동 합 균일
Token bucket 버킷 충전·소비 burst 허용 + 평균 상한
Leaky bucket 고정 속도 배출 smooth 출력
적합
IP 익명·공개 API (NAT 공유 주의)
API key / client_id B2B·파트너
user_id 로그인 후 공정 할당
route + key heavy endpoint 별도 한도
# code/rate-limit-middleware.example.py
# Token-bucket sketch (Redis INCR + TTL or dedicated lib)
LIMIT = 100
WINDOW_SEC = 60

def check_rate_limit(redis, key: str) -> bool:
    count = redis.incr(f"rl:{key}")
    if count == 1:
        redis.expire(f"rl:{key}", WINDOW_SEC)
    return count <= LIMIT

# HTTP 429 Too Many Requests
# Retry-After: 30
# X-RateLimit-Remaining: 0
응답  
429 한도 초과 — Retry-After 헤더
503 upstream 과부하 — rate limit과 구분
헤더 X-RateLimit-Limit, Remaining, Reset (관례)
  • Gateway에서 먼저 — 앱 도달 차단 (비용↓)
  • 비용 민감 API — LLM·결제·SMS per-call 한도
  • 13편 키 유출 — limit + 즉시 revoke + rotate

3. Input validation

경계에서 거부 — handler·ORM 에 스키마로 형식·범위 고정.

원칙  
Allowlist enum·regex·known fields — denylist만 불충분
타입 string vs number — "123" vs 123 혼동 방지
크기 body max, array length, string length
필드 additionalProperties: false — mass assignment 방지
정규화 Unicode NFKC, trim — 우회 축소
# code/input-validation-safe.py
from pydantic import BaseModel, Field, ConfigDict

class CreateUserRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    email: str = Field(max_length=254)
    name: str = Field(min_length=1, max_length=100)
    # role is NOT client-writable — set server-side only

def create_user(body: CreateUserRequest):
  role = "member"  # never trust body.role
  ...
# code/input-validation-vulnerable.py
# VULNERABLE — do not use
def create_user(body: dict):
    user = User(**body)  # role=admin from client
    db.save(user)
OWASP 연계  
Injection (9편) 검증 + prepared statement
BAC / mass assignment (11편) 허용 필드만 바인딩
SSRF (12편) URL allowlist·private IP 차단
  • 클라이언트 검증 — UX용; 서버 검증 필수
  • GraphQL — query depth·complexity limit 별도
  • 파일 업로드 — MIME sniff·size·virus scan

4. CORS

CORS(Cross-Origin Resource Sharing)브라우저가 다른 Origin 응답을 노출할지 말지 정하는 정책. 서버 간 호출에는 적용 안 됨.

헤더  
Access-Control-Allow-Origin 허용 origin (와일드카드 주의)
Access-Control-Allow-Methods GET, POST, …
Access-Control-Allow-Headers Authorization, Content-Type
Access-Control-Allow-Credentials 쿠키·인증 헤더
Access-Control-Max-Age preflight 캐시
Simple GET (no preflight):  browser adds Origin → server reflects Allow-Origin

Non-simple (JSON POST + Authorization):
  1. OPTIONS preflight
  2. server 204 + Allow-* headers
  3. actual request
# code/cors-nginx.example
# Reflect allowed origin — never * with credentials
set $cors_origin "";
if ($http_origin ~* ^https://(app|admin)\.example\.com$) {
    set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Vary Origin always;
오설정 위험
Allow-Origin: * + credentials 브라우저 거부 (스펙)
모든 Origin 반사 ($http_origin 무비판) 악성 사이트 허용
CORS만 믿고 인가 생략 curl·Postman은 CORS 무시
preflight 캐시 과다 정책 변경 지연 반영
  • API 전용 (모바일·서버 only) — CORS 불필요; 인증·인가가 핵심
  • 쿠키 세션 — SameSite·CSRF (10편) + CORS 함께
  • Network TLS (3편) — CORS 대체 아님

5. API 키·버전·문서

통제  
API key 헤더 X-API-Key13편 rotation·scope
OAuth scope fine-grained 리소스 접근 (6~7편)
Versioning /v1 — breaking change 격리
Deprecation Sunset 헤더·문서 기한
Error body 스택·내부 ID 노출 금지 (12편 misconfig)
# Minimal safe error
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{"type":"validation_error","title":"Invalid request","status":400}

6. 체크리스트

항목 확인
Rate limit gateway + app, per key/user/route
429 Retry-After, monitoring on throttle
Schema OpenAPI/Pydantic, extra fields forbidden
Size max body, pagination cap
CORS explicit allowlist, no blind origin reflect
Auth every endpoint — no “public by omission”
Keys vault, rotate, per-env (13편)
Logs no full API key in access log
안티패턴  
UI만 검증 서버 우회 trivial
CORS = security 서버 인가 없으면 무의미
무제한 list API limit=100 cap
rate limit only IP 공유 NAT 차단 과다

7. 정리

  • API — 자동화·남용에 취약; gateway·앱 다층 방어
  • Rate limit — 가용성·비용·유출 키 피해 제한
  • Validation — 경계에서 스키마·allowlist; Injection·BAC 연계
  • CORS브라우저 정책; 인가 대체 불가
  • 13편 시크릿 + 5~8편 인증·인가 + 본편 운영 통제 = API 최소 세트

다음에 다룰 것

  • 로깅과 감사
  • PII 마스킹, Observability 연계

해당 내용은 OWASP API Security Top 10, OWASP REST Security Cheat Sheet 의 내용을 기반으로 합니다.

'애플리케이션 보안' 카테고리의 다른 글

보안 체크리스트  (0) 2026.07.03
로깅과 감사  (0) 2026.07.02
시크릿 관리  (0) 2026.06.30
OWASP Top 10  (0) 2026.06.29
Broken Access Control  (0) 2026.06.28