개념
API용 목록이 따로 있는 이유
OWASP는 웹 애플리케이션 Top 10과 별개로 API Security Top 10 을 낸다. 현재 판은 2023년판 이고 항목 ID는 API1:2023 ~ API10:2023 형식이다. 그 전 판은 2019년판이므로,
사내 문서나 벤더 리포트가 API1:2019를 인용하고 있다면 오래된 매핑을 쓰고 있는 것이다. 두 판 사이에 항목이 바뀌었으므로 번호만 보고 같은 것이라 생각하면 안 된다.
목록이 따로 있는 이유는 API에서 결함의 무게 중심이 다르기 때문이다. 화면이 없으니 XSS의 비중이 줄고, 대신 클라이언트가 보내는 모든 것을 서버가 스스로 판단해야 하는 상황이 늘어난다. 그래서 인가 결함이 1·3·5위를 차지한다.
| ID | 항목 | 한 줄 요약 |
|---|---|---|
| API1:2023 | Broken Object Level Authorization (BOLA) | 객체 단위 소유권 검사 누락 — 남의 리소스 접근 |
| API2:2023 | Broken Authentication | 인증 자체의 결함 — 토큰 검증·재설정·무차별 대입 |
| API3:2023 | Broken Object Property Level Authorization | 객체의 특정 속성에 대한 읽기/쓰기 권한 누락 |
| API4:2023 | Unrestricted Resource Consumption | 요청량·페이지 크기·업로드·과금 자원의 제한 부재 |
| API5:2023 | Broken Function Level Authorization (BFLA) | 기능(역할) 단위 인가 누락 — 관리자 기능 호출 성공 |
| API6:2023 | Unrestricted Access to Sensitive Business Flows | 업무 흐름 자체의 자동화 남용(매크로 구매, 대량 가입) |
| API7:2023 | Server Side Request Forgery (SSRF) | 서버가 클라이언트가 준 URL을 그대로 요청 |
| API8:2023 | Security Misconfiguration | 설정 결함 — CORS, 기본 계정, 디버그 응답, 헤더 |
| API9:2023 | Improper Inventory Management | 버전·환경·문서 관리 실패 — 섀도/좀비 API |
| API10:2023 | Unsafe Consumption of APIs | 우리가 호출하는 외부 API의 응답을 무비판적으로 신뢰 |
API3은 2019년판의 "Excessive Data Exposure"와 "Mass Assignment"가 한 항목으로 합쳐진 것이다. 읽기 방향(응답에 불필요한 필드가 섞여 나감)과 쓰기 방향(요청에 넣으면 안 되는 필드가 반영됨)을 같은 원인으로 본다는 뜻이다. 테스트할 때는 두 방향을 따로 케이스로 만든다.
API1(BOLA)과 API5(BFLA)를 구분해 검증한다
레슨 2의 수평·수직 권한 상승이 여기서 이름을 얻는다.
BOLA (API1) — "이 객체가 내 것인가"를 안 본다
GET /api/v1/invoices/9821 내 계정으로 호출 → 남의 인보이스가 나온다
BFLA (API5) — "이 기능을 쓸 자격이 있는가"를 안 본다
POST /api/v1/admin/users/13/suspend 일반 계정으로 호출 → 성공한다두 케이스를 만드는 방법이 다르다.
| BOLA 케이스 만들기 | BFLA 케이스 만들기 | |
|---|---|---|
| 출발점 | 식별자를 받는 모든 엔드포인트 | 역할이 갈리는 모든 엔드포인트 |
| 필요한 준비 | 서로 다른 소유자의 리소스 2세트 | 최소 권한 계정 + 관리자 API 목록 |
| 조작 대상 | 경로 파라미터, 쿼리, 본문의 ID, 배열 안의 ID | HTTP 메서드, 경로 접두사, 역할 헤더 |
| 자주 빠지는 곳 | 배치/일괄 API의 ID 배열, 파일 다운로드 경로 | 문서에 없는 내부 관리 엔드포인트 |
BFLA에서 메서드를 바꿔 보는 것 을 잊기 쉽다. GET /api/v1/users/13은 403인데 DELETE나 PUT은 라우팅이 다른 핸들러를 타면서 검사를 건너뛰는 경우가 실제로 있다.
HEAD·OPTIONS도 포함해 메서드 전수를 확인한다.
API4 — 자원 소비, QA가 가장 잘 만드는 케이스
모듈 16에서 배운 성능 테스트 감각이 그대로 보안 케이스가 되는 영역이다. 질문은 하나다. "이 API가 제한 없이 비싸질 수 있는 입력이 있는가."
RC-01 페이지 크기 ?limit=1000000 → 서버가 상한으로 잘라야 한다
RC-02 정렬·필터 조합 인덱스 없는 컬럼 정렬 요청 → 타임아웃/풀스캔
RC-03 깊은 페이지네이션 ?offset=5000000 → 응답 시간 급증
RC-04 파일 업로드 용량·확장자·동시 업로드 수 제한
RC-05 압축 폭탄 작은 요청이 큰 처리로 전개되는 경로
RC-06 GraphQL 중첩 질의 깊이/복잡도 제한 (모듈 12 레슨 6 참조)
RC-07 과금 자원 호출 SMS·이메일 발송, 외부 API 호출이 붙은 엔드포인트
RC-08 인증 전 엔드포인트 로그인·가입·인증번호 발송의 속도 제한RC-07이 사업적으로 가장 아픈 항목이다. 인증번호 재발송 API에 속도 제한이 없으면 공격자는 데이터를 못 가져가도 문자 발송 비용을 무한히 태울 수 있다. 이런 케이스는 스테이징에서 소규모로 확인하고, 실측값(요청 N회에 실제 발송 N회, 건당 단가)을 보고서에 넣는다. 금액으로 환산된 영향은 우선순위를 즉시 바꾼다.
API6 — 업무 흐름의 남용
API6은 "취약점"처럼 생기지 않아서 가장 자주 무시된다. 각 요청은 정상이고 권한도 맞다. 문제는 그 흐름을 사람이 아닌 스크립트가 반복한다는 것 이다.
| 흐름 | 남용 시나리오 | 점검 질문 |
|---|---|---|
| 한정 상품 구매 | 매크로가 재고를 전량 선점 | 1계정·1IP·1기기당 제한이 있는가 |
| 회원 가입 | 프로모션 쿠폰 목적 대량 가입 | 동일 기기·유사 이메일 패턴 탐지가 있는가 |
| 예약 생성·취소 | 좌석 점유 후 반복 취소 | 취소율 기반 제한이 있는가 |
| 후기 작성 | 평점 조작 | 구매 이력 연결과 작성 빈도 제한이 있는가 |
QA의 점검 방식은 정상 속도와 비정상 속도의 경계를 실측하는 것 이다. "10초에 5번까지는 통과, 6번째부터 429"처럼 관측된 임계값을 적고, 그 임계값이 기획이 의도한 값인지 확인한다. 기획서에 임계값이 아예 없다면 그것 자체가 결함이다.
API7(SSRF)과 API10 — 방향이 반대인 두 항목
API7 SSRF : 클라이언트가 준 URL을 우리 서버가 요청한다
프로필 이미지 URL, 웹훅 등록, PDF 렌더링, 링크 미리보기
API10 Unsafe Consumption : 우리가 호출한 외부 API의 응답을 그대로 믿는다
결제사 콜백, 배송사 조회 결과, 외부 인증 서버 응답SSRF 점검 케이스는 "우리 서버가 내부 주소로 요청하는가"를 본다. 여기서 중요한 것은 실제로 내부망을 뒤지는 것이 아니라, 요청이 차단되는지만 확인하는 것 이다. 승인된 테스트 대상 안에서, 응답 코드·응답 시간·서버 로그로 차단 여부를 판정하면 충분하다.
SSRF-01 사설 대역 주소를 URL로 제출 → 거부되어야 한다
SSRF-02 리다이렉트로 사설 대역으로 유도 → 최종 목적지도 검사하는가
SSRF-03 DNS가 사설 IP로 해석되는 도메인 → 해석 결과 기준으로 막는가
SSRF-04 file:// 등 비 HTTP 스킴 → 허용 스킴 화이트리스트가 있는가
SSRF-05 응답 본문이 그대로 노출되는가 → 노출되면 영향이 크게 커진다API10은 QA가 잘 보는 자리다. 외부 연동 테스트에서 "정상 응답"만 넣어 보는 습관 이 이 항목을 놓치게 한다. 모듈 12(레슨 5)의 컨슈머 주도 계약 테스트에서 배운 대로, 외부가 보낼 수 있는 이상한 응답 — 필드 누락, 타입 불일치, 음수 금액, 과도하게 긴 문자열, 리다이렉트, 500 — 을 스텁으로 주입해 우리 쪽이 어떻게 되는지 본다.
API9 — 섀도 API와 좀비 API
문서에 있는 API만 테스트하면 문서에 없는 API는 영원히 테스트되지 않는다.
| 이름 | 정의 | 찾는 법 |
|---|---|---|
| 섀도(Shadow) API | 문서·스펙에 없지만 살아 있는 엔드포인트 | 게이트웨이 접근 로그의 경로 집계 |
| 좀비(Zombie) API | 구버전이라 폐기했다고 믿지만 응답하는 엔드포인트 | /v1, /v2 병행 호출 비교 |
| 미공개 환경 | 스테이징·데모가 인터넷에 열려 있음 | 인증서 투명성 로그, 서브도메인 목록(사내 자산 대장 기준) |
모듈 12(레슨 4)에서 OpenAPI 계약 테스트를 배웠다. 그 자산을 여기서 반대 방향으로 쓴다. 계약 테스트는 "스펙에 있는 것이 구현과 맞는가"를 보지만, API9 점검은 "구현에 있는데 스펙에 없는 것은 무엇인가" 를 본다.
# 게이트웨이 접근 로그의 경로를 정규화해 집계하고 스펙과 대조하는 골격
# (사내 로그 접근 권한이 있는 환경에서만 실행한다)
awk '{print $7}' access.log \
| sed -E 's#/[0-9]+#/{id}#g; s#\?.*##' \
| sort | uniq -c | sort -rn > observed_paths.txt
# openapi.yaml 에서 선언된 경로 목록
python3 - <<'PY' > declared_paths.txt
import yaml, sys
spec = yaml.safe_load(open("openapi.yaml"))
for p in sorted(spec.get("paths", {})):
print(p)
PY
# 관측되지만 선언되지 않은 경로 = 섀도 API 후보
comm -23 <(awk '{print $2}' observed_paths.txt | sort -u) <(sort -u declared_paths.txt)이 대조를 분기마다 한 번 돌려 목록을 내놓는 것만으로도 API9 대응의 대부분이 끝난다. 후보가 나오면 각각에 대해 소유자·용도·폐기 가능 여부를 묻고, 남길 것은 스펙에 등록해 계약 테스트 대상에 넣는다.
점검 케이스로의 번역표
열 항목을 그대로 외울 필요는 없다. 엔드포인트 하나를 받으면 어떤 질문을 순서대로 던지는가 를 몸에 익히는 편이 실용적이다.
| 순서 | 질문 | 대응 항목 |
|---|---|---|
| 1 | 인증 없이 호출하면? | API2 |
| 2 | ID를 남의 것으로 바꾸면? | API1 |
| 3 | 권한이 낮은 계정으로 호출하면? 메서드를 바꾸면? | API5 |
| 4 | 응답에 필요 없는 필드가 있는가? 요청에 넣으면 안 되는 필드가 반영되는가? | API3 |
| 5 | 크게·많이·빠르게 요청하면? | API4, API6 |
| 6 | URL을 받는 파라미터가 있는가? | API7 |
| 7 | 오류 응답이 내부 정보를 흘리는가? CORS·헤더 설정은? | API8 |
| 8 | 이 경로가 스펙에 있는가? 구버전도 살아 있는가? | API9 |
| 9 | 이 API가 호출하는 외부 응답이 비정상이면? | API10 |
판정 — API 결함의 등급을 정하는 세 질문
레슨 1·2에서 쓴 기준이 API에서도 같다. 다만 API는 자동화가 쉽다는 점 때문에 규모 요인이 특히 크게 작동한다.
(1) 인증이 필요한가, 아닌가
비인증 BOLA는 거의 항상 Critical, 인증 후 BOLA는 규모에 따라 달라진다
(2) 식별자를 열거할 수 있는가
순번이면 전량, UUID면 다른 경로로 ID를 얻을 수 있는지에 달려 있다
(3) 보상 통제가 실제로 작동하는가
WAF 규칙, 속도 제한, 이상 탐지가 "설정되어 있다"가 아니라
실제 요청을 막았는지 로그로 확인한다(3)을 확인하지 않고 "WAF가 있으니 중간 등급"으로 낮추는 것이 흔한 실수다. 보상 통제는 테스트로 확인된 것만 감점 요인으로 인정한다 는 규칙을 팀 기준으로 못 박아 두면 논쟁이 줄어든다.
실무에서 왜 필요한가
API는 화면보다 수명이 길고, 화면보다 조용히 늘어난다. 모바일 앱 v1을 위해 만든 엔드포인트는 앱이 v5가 되어도 살아 있고, 파트너 연동용으로 급히 뚫은 경로는 문서에 없는 채로 남는다. 화면 기준 테스트 계획은 이 증가를 따라가지 못한다. 엔드포인트 인벤토리를 테스트 계획의 1급 자산으로 두는 것 이 API 보안 테스트의 출발점이고, 그건 도구가 아니라 프로세스의 문제다.
또 API 보안 케이스는 회귀에 남기기 가장 쉬운 보안 테스트 다. UI 보안 테스트는 화면이 바뀌면 깨지지만, API 인가 매트릭스는 계약이 바뀌지 않는 한 유지된다. 레슨 2에서 만든 3축 매트릭스에 이 레슨의 API4·API3 케이스를 얹으면, 그대로 CI에서 도는 보안 회귀 스위트가 된다.