API Security Checklist — 웹앱 API 보안 체크리스트
backend-dev 에이전트가 API 개발 시 활용하는 OWASP 기반 보안 체크리스트, 인증 패턴, 방어 코드 가이드.
대상 에이전트
backend-dev — 이 스킬의 보안 체크리스트를 API 구현에 직접 적용한다.
OWASP API Security Top 10 점검
| 순위 |
취약점 |
점검 항목 |
방어 |
| A1 |
BOLA (객체 수준 인가 결함) |
다른 사용자의 리소스 접근 가능? |
모든 엔드포인트에서 객체 소유권 검증 |
| A2 |
인증 결함 |
약한 비밀번호, 무제한 로그인 시도? |
bcrypt 해싱, Rate Limit, MFA |
| A3 |
객체 속성 수준 인가 |
숨겨야 할 필드 노출? |
응답 DTO로 필드 필터링 |
| A4 |
무제한 리소스 소비 |
대량 요청으로 서버 마비? |
Rate Limiting, 페이지네이션 강제 |
| A5 |
기능 수준 인가 결함 |
관리자 API를 일반 유저가 호출? |
RBAC 미들웨어 |
| A6 |
서버 사이드 요청 위조 (SSRF) |
외부 URL 입력으로 내부 접근? |
URL 화이트리스트, 내부 IP 차단 |
| A7 |
보안 설정 오류 |
디버그 모드 노출, 기본 계정? |
프로덕션 설정 분리, 헤더 점검 |
| A8 |
비즈니스 흐름 결함 |
정상 API를 비정상 순서로 호출? |
상태 머신 검증, 비즈니스 규칙 서버측 |
| A9 |
취약 자산 관리 |
미사용 API, 구버전 노출? |
API 인벤토리, 버전 폐기 정책 |
| A10 |
안전하지 않은 API 소비 |
외부 API 응답 무조건 신뢰? |
외부 응답 검증, 타임아웃 설정 |
인증 (Authentication) 패턴
JWT 기반 인증
| 항목 |
권장 설정 |
| Access Token 만료 |
15~30분 |
| Refresh Token 만료 |
7~14일 |
| 알고리즘 |
RS256 (비대칭) 또는 HS256 (대칭) |
| 저장소 |
httpOnly + secure + sameSite cookie |
| Payload |
최소 정보만 (userId, role) — PII 금지 |
| 갱신 전략 |
Silent Refresh 또는 Rotation |
비밀번호 정책
- 최소 8자, 대소문자+숫자+특수문자 권장 (강제보다 강도 표시)
- bcrypt (cost factor 12+) 또는 Argon2id
- 비밀번호 히스토리 (최근 5개 재사용 금지)
- 로그인 실패 5회 시 임시 잠금 (15분) 또는 CAPTCHA
인가 (Authorization) 패턴
RBAC (역할 기반)
역할 정의: admin, manager, user, viewer
권한 매핑:
admin → *.* (전체)
manager → resource.create, resource.read, resource.update
user → resource.create (own), resource.read (own)
viewer → resource.read (public)
미들웨어 체인
요청 → [Rate Limit] → [인증: JWT 검증] → [인가: 역할 확인] → [입력 검증] → 핸들러
입력 검증 체크리스트
| 검증 항목 |
방법 |
도구 |
| 타입 검증 |
스키마 검증 |
Zod, Joi, class-validator |
| 길이 제한 |
최소/최대 길이 |
스키마에 min/max |
| 패턴 매칭 |
이메일, URL, 전화번호 |
정규식 + 라이브러리 |
| 범위 검증 |
숫자 범위, 날짜 범위 |
min/max 값 |
| 열거형 |
허용된 값 목록 |
enum 타입 |
| SQL Injection |
파라미터화 쿼리 |
ORM (Prisma, TypeORM) |
| XSS |
HTML 이스케이핑 |
DOMPurify (클라이언트), 서버 이스케이프 |
| Path Traversal |
경로 정규화 |
path.resolve + 화이트리스트 |
| 파일 업로드 |
타입/크기 검증 |
MIME 타입 + 매직넘버 검증 |
HTTP 보안 헤더
| 헤더 |
값 |
목적 |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
HTTPS 강제 |
X-Content-Type-Options |
nosniff |
MIME 스니핑 방지 |
X-Frame-Options |
DENY 또는 SAMEORIGIN |
클릭재킹 방지 |
Content-Security-Policy |
default-src 'self' |
XSS 방지 |
X-XSS-Protection |
0 (CSP로 대체) |
레거시 |
Referrer-Policy |
strict-origin-when-cross-origin |
리퍼러 정보 제한 |
Permissions-Policy |
camera=(), microphone=() |
브라우저 기능 제한 |
CORS 설정 가이드
| 환경 |
설정 |
| 개발 |
origin: 'http://localhost:3000' |
| 프로덕션 |
origin: ['https://example.com'] — 도메인 명시 |
| 금지 |
origin: '*' + credentials: true — 보안 위험 |
필수 설정:
methods: 필요한 메서드만 허용
allowedHeaders: 필요한 헤더만
credentials: 쿠키 필요 시에만 true
maxAge: 프리플라이트 캐싱 (86400초)
Rate Limiting 전략
| 대상 |
제한 |
구현 |
| 인증 엔드포인트 |
5회/분/IP |
IP 기반 |
| API 일반 |
100회/분/사용자 |
토큰 기반 |
| 파일 업로드 |
10회/시간/사용자 |
토큰 기반 |
| 비인증 API |
30회/분/IP |
IP 기반 |
응답 헤더
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1609459200
Retry-After: 60 (429 응답 시)
에러 응답 보안
프로덕션 에러 응답 규칙
- 내부 구현 세부사항 절대 노출 금지 (스택 트레이스, SQL 쿼리)
- 일관된 에러 형식 사용
- 열거 공격 방지: 로그인 실패 시 "이메일 또는 비밀번호가 올바르지 않습니다" (어느 것이 틀렸는지 X)
에러 응답 형식
{
"error": {
"code": "VALIDATION_ERROR",
"message": "입력값이 올바르지 않습니다",
"details": [
{"field": "email", "message": "유효한 이메일을 입력하세요"}
]
}
}
민감 데이터 처리
| 데이터 유형 |
저장 |
전송 |
로깅 |
| 비밀번호 |
bcrypt 해시만 |
HTTPS only |
절대 금지 |
| API Key |
환경변수 |
헤더 (Authorization) |
마스킹 (앞4자리만) |
| 개인정보 (PII) |
암호화 (AES-256) |
HTTPS only |
마스킹 |
| 신용카드 |
토큰화 (PG사 위임) |
PG사 SDK |
절대 금지 |
| 세션/토큰 |
httpOnly cookie |
HTTPS only |
절대 금지 |
1---2name: api-security-checklist-23description: 웹앱 API 보안 체크리스트. OWASP Top 10 기반 취약점 점검, 인증/인가 패턴, 입력 검증, Rate Limiting, CORS, CSRF, SQL Injection 방어를 제공하는 backend-dev 확장 스킬. 'API 보안', 'OWASP', '인증 구현', 'SQL Injection', 'XSS 방어', 'CORS 설정', '보안 체크리스트' 등 백엔드 보안 설계 시 사용한다. 단, 침투 테스트 수행이나 WAF 구성은 이 스킬의 범위가 아니다.4---56# API Security Checklist — 웹앱 API 보안 체크리스트78backend-dev 에이전트가 API 개발 시 활용하는 OWASP 기반 보안 체크리스트, 인증 패턴, 방어 코드 가이드.910## 대상 에이전트1112`backend-dev` — 이 스킬의 보안 체크리스트를 API 구현에 직접 적용한다.1314## OWASP API Security Top 10 점검1516| 순위 | 취약점 | 점검 항목 | 방어 |17|------|--------|----------|------|18| A1 | **BOLA** (객체 수준 인가 결함) | 다른 사용자의 리소스 접근 가능? | 모든 엔드포인트에서 객체 소유권 검증 |19| A2 | **인증 결함** | 약한 비밀번호, 무제한 로그인 시도? | bcrypt 해싱, Rate Limit, MFA |20| A3 | **객체 속성 수준 인가** | 숨겨야 할 필드 노출? | 응답 DTO로 필드 필터링 |21| A4 | **무제한 리소스 소비** | 대량 요청으로 서버 마비? | Rate Limiting, 페이지네이션 강제 |22| A5 | **기능 수준 인가 결함** | 관리자 API를 일반 유저가 호출? | RBAC 미들웨어 |23| A6 | **서버 사이드 요청 위조 (SSRF)** | 외부 URL 입력으로 내부 접근? | URL 화이트리스트, 내부 IP 차단 |24| A7 | **보안 설정 오류** | 디버그 모드 노출, 기본 계정? | 프로덕션 설정 분리, 헤더 점검 |25| A8 | **비즈니스 흐름 결함** | 정상 API를 비정상 순서로 호출? | 상태 머신 검증, 비즈니스 규칙 서버측 |26| A9 | **취약 자산 관리** | 미사용 API, 구버전 노출? | API 인벤토리, 버전 폐기 정책 |27| A10 | **안전하지 않은 API 소비** | 외부 API 응답 무조건 신뢰? | 외부 응답 검증, 타임아웃 설정 |2829## 인증 (Authentication) 패턴3031### JWT 기반 인증3233| 항목 | 권장 설정 |34|------|----------|35| Access Token 만료 | 15~30분 |36| Refresh Token 만료 | 7~14일 |37| 알고리즘 | RS256 (비대칭) 또는 HS256 (대칭) |38| 저장소 | httpOnly + secure + sameSite cookie |39| Payload | 최소 정보만 (userId, role) — PII 금지 |40| 갱신 전략 | Silent Refresh 또는 Rotation |4142### 비밀번호 정책43- 최소 8자, 대소문자+숫자+특수문자 권장 (강제보다 강도 표시)44- bcrypt (cost factor 12+) 또는 Argon2id45- 비밀번호 히스토리 (최근 5개 재사용 금지)46- 로그인 실패 5회 시 임시 잠금 (15분) 또는 CAPTCHA4748## 인가 (Authorization) 패턴4950### RBAC (역할 기반)51```52역할 정의: admin, manager, user, viewer53권한 매핑:54 admin → *.* (전체)55 manager → resource.create, resource.read, resource.update56 user → resource.create (own), resource.read (own)57 viewer → resource.read (public)58```5960### 미들웨어 체인61```62요청 → [Rate Limit] → [인증: JWT 검증] → [인가: 역할 확인] → [입력 검증] → 핸들러63```6465## 입력 검증 체크리스트6667| 검증 항목 | 방법 | 도구 |68|----------|------|------|69| **타입 검증** | 스키마 검증 | Zod, Joi, class-validator |70| **길이 제한** | 최소/최대 길이 | 스키마에 min/max |71| **패턴 매칭** | 이메일, URL, 전화번호 | 정규식 + 라이브러리 |72| **범위 검증** | 숫자 범위, 날짜 범위 | min/max 값 |73| **열거형** | 허용된 값 목록 | enum 타입 |74| **SQL Injection** | 파라미터화 쿼리 | ORM (Prisma, TypeORM) |75| **XSS** | HTML 이스케이핑 | DOMPurify (클라이언트), 서버 이스케이프 |76| **Path Traversal** | 경로 정규화 | path.resolve + 화이트리스트 |77| **파일 업로드** | 타입/크기 검증 | MIME 타입 + 매직넘버 검증 |7879## HTTP 보안 헤더8081| 헤더 | 값 | 목적 |82|------|---|------|83| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | HTTPS 강제 |84| `X-Content-Type-Options` | `nosniff` | MIME 스니핑 방지 |85| `X-Frame-Options` | `DENY` 또는 `SAMEORIGIN` | 클릭재킹 방지 |86| `Content-Security-Policy` | `default-src 'self'` | XSS 방지 |87| `X-XSS-Protection` | `0` (CSP로 대체) | 레거시 |88| `Referrer-Policy` | `strict-origin-when-cross-origin` | 리퍼러 정보 제한 |89| `Permissions-Policy` | `camera=(), microphone=()` | 브라우저 기능 제한 |9091## CORS 설정 가이드9293| 환경 | 설정 |94|------|------|95| 개발 | `origin: 'http://localhost:3000'` |96| 프로덕션 | `origin: ['https://example.com']` — 도메인 명시 |97| 금지 | `origin: '*'` + `credentials: true` — 보안 위험 |9899필수 설정:100- `methods`: 필요한 메서드만 허용101- `allowedHeaders`: 필요한 헤더만102- `credentials`: 쿠키 필요 시에만 true103- `maxAge`: 프리플라이트 캐싱 (86400초)104105## Rate Limiting 전략106107| 대상 | 제한 | 구현 |108|------|------|------|109| 인증 엔드포인트 | 5회/분/IP | IP 기반 |110| API 일반 | 100회/분/사용자 | 토큰 기반 |111| 파일 업로드 | 10회/시간/사용자 | 토큰 기반 |112| 비인증 API | 30회/분/IP | IP 기반 |113114### 응답 헤더115```116X-RateLimit-Limit: 100117X-RateLimit-Remaining: 95118X-RateLimit-Reset: 1609459200119Retry-After: 60 (429 응답 시)120```121122## 에러 응답 보안123124### 프로덕션 에러 응답 규칙125- 내부 구현 세부사항 절대 노출 금지 (스택 트레이스, SQL 쿼리)126- 일관된 에러 형식 사용127- 열거 공격 방지: 로그인 실패 시 "이메일 또는 비밀번호가 올바르지 않습니다" (어느 것이 틀렸는지 X)128129### 에러 응답 형식130```json131{132 "error": {133 "code": "VALIDATION_ERROR",134 "message": "입력값이 올바르지 않습니다",135 "details": [136 {"field": "email", "message": "유효한 이메일을 입력하세요"}137 ]138 }139}140```141142## 민감 데이터 처리143144| 데이터 유형 | 저장 | 전송 | 로깅 |145|-----------|------|------|------|146| 비밀번호 | bcrypt 해시만 | HTTPS only | 절대 금지 |147| API Key | 환경변수 | 헤더 (Authorization) | 마스킹 (앞4자리만) |148| 개인정보 (PII) | 암호화 (AES-256) | HTTPS only | 마스킹 |149| 신용카드 | 토큰화 (PG사 위임) | PG사 SDK | 절대 금지 |150| 세션/토큰 | httpOnly cookie | HTTPS only | 절대 금지 |