Skip to main content

비즈니스 로직 결함

점검 목적​

가격·쿠폰·환불·가입·승인 같은 기능이 정해진 조건과 순서대로 처리되는지 확인한다. 화면이 허용하는 입력만 믿지 않고, Network 요청과 JavaScript 분석에서 찾은 필드·상태·숨은 API를 서버가 다시 검증하는지 본다.

  • 같은 요청을 동시에 처리할 때만 발생하는 문제는 Race Condition에서 다룬다.
  • 다른 사용자 객체나 관리자 기능 접근은 권한 검증 / IDOR에서 다룬다.
  • 사용자 열거·MFA·재설정 흐름은 인증에서 다룬다.

유형 구분​

유형특징실무 판단
값 신뢰가격·수량·할인·배송비를 클라이언트 값으로 처리함필드 제거와 정상 범위 밖 값 비교
단계 우회이전 단계 없이 완료 API를 호출할 수 있음정상·생략·역순 흐름 비교
조건 우회쿠폰·포인트·가입 혜택의 대상·기간·횟수 검증이 빠짐한 조건씩 바꿔 후속 상태 확인
상태 위반취소·배송·환불 상태가 정의되지 않은 방향으로 바뀜현재 상태와 허용 전이표 작성
정산 불일치결제액·환불액·권한 회수가 서로 맞지 않음금액과 상품·서비스 권한을 함께 확인

진단 절차​

Step 1. 정상 흐름과 규칙 기록​

테스트 계정으로 기능을 한 번 정상 수행하고 요청 순서와 전후 상태를 저장한다.

상품 선택 → 주문 미리보기 → 주문 생성 → 결제 → 확정
쿠폰 발급 → 조건 확인 → 적용 → 사용 완료
구매 → 사용 권한 부여 → 환불 → 권한 회수
신청 작성 → 검토 → 승인 → 결과 공개

각 흐름에서 반드시 지켜져야 하는 규칙을 한 문장으로 적는다.

최종 가격은 서버의 상품 가격과 할인 정책으로 계산된다.
결제가 완료된 주문만 확정된다.
환불 합계는 결제액을 넘지 않는다.
환불된 디지털 상품의 사용 권한은 회수된다.

Step 2. Network 요청과 서버 결정값 분리​

Burp와 DevTools Network에서 실제 요청을 저장하고, 사용자가 정할 값과 서버가 정해야 할 값을 나눈다.

요청값기본 판단
productId, 배송지 선택사용자가 선택 가능
price, discount, total서버 재계산 필요
ownerId, role, status서버 문맥으로 결정 필요
쿠폰 코드·포인트 사용량사용자 입력이지만 조건·한도는 서버 검증 필요
결제 결과·환불 가능 여부외부 결제 결과와 서버 상태로 결정 필요

Step 3. JavaScript에서 숨은 단서 보완​

JavaScript 분석의 절차로 현재 화면과 동적 chunk를 확인한다.

API 경로와 HTTP Method
request body 기본값과 숨은 필드
가격·배송비·할인 계산식
PENDING, PAID, SHIPPED 같은 상태 상수
nextStep, confirm, complete 같은 단계 함수
관리자·실험 기능 route와 feature flag

JS에 코드가 있다는 사실은 후보일 뿐이다. Network에서 실제 요청 형식을 확인하고 서버에 한 항목씩 재전송한다.

Step 4. 가장 단순한 변형부터 비교​

정상 요청을 복제한 뒤 한 번에 하나만 바꾼다.

서버 결정값으로 보이는 필드 제거
수량을 1 → 0 → -1로 변경
쿠폰 조건 하나만 불일치하게 변경
중간 단계 없이 다음 API 직접 호출
현재 상태에서 허용되지 않는 다음 상태 요청

극단적인 큰 값보다 0, -1, 경계값 바로 아래·위처럼 결과를 해석하기 쉬운 값을 먼저 사용한다.

Step 5. 조건 조합과 우회 확인​

단일 조건 검증이 정상일 때만 조합을 본다.

쿠폰 적용 후 상품 제거
배송지 변경 후 배송비 재계산 여부
환불 후 다운로드·구독 권한 유지 여부
탈퇴·재가입 후 첫 가입 혜택 복구 여부
주문 취소 후 결제·배송 API 직접 호출

Step 6. 최종 상태로 판정​

응답 메시지보다 주문 상세·결제 이력·잔액·쿠폰 사용 기록·권한 상태를 확인한다. 데이터를 초기화해 같은 결과가 반복되는지 보고, 필요한 최소 단계만 남긴다.

상황별 빠른 선택​

현재 기능첫 확인
주문 body에 price, total이 있음필드 제거 후 서버 재계산 여부
다단계 신청·결제마지막 API 직접 호출과 이전 단계 검증
쿠폰·포인트만료·대상·최소 금액 조건을 하나씩 변경
환불·취소결제액 상한과 상품·서비스 권한 회수
가입 혜택·추천동일 사용자 판단 기준과 재가입 처리
상태값이 body에 있음허용되지 않은 이전·다음 상태 요청
화면에는 API가 보이지 않음JS route·상태 함수와 Network Initiator 확인

페이로드 노트​

1. 가격·수량·할인값 신뢰​

이럴 때 사용: 주문 요청에 서버가 계산해야 할 price, total, discount, shippingFee가 포함된다.

먼저 필드를 제거해 서버가 값을 다시 계산하는지 본다. 제거할 수 없다면 정상값과 작은 경계값을 비교한다.

POST /api/order HTTP/1.1
Content-Type: application/json

{
"productId": 42,
"quantity": 1,
"price": 1
}

수량은 1 → 0 → -1 순으로 확인한다.

{
"productId": 42,
"quantity": 0
}

JS에서 할인 필드를 찾았을 때만 추가한다.

{
"productId": 42,
"discountRate": 100,
"discountAmount": 1000
}

확인할 것: 응답의 표시 금액이 아니라 주문 상세와 결제 요청에 저장된 최종 금액을 본다. 서버가 상품 ID로 가격을 다시 계산하고 잘못된 수량을 거부하면 정상이다. 큰 수와 부동소수점은 작은 경계값에서 이상이 보일 때만 조건부로 확인한다.

2. 결제·주문 단계 우회​

이럴 때 사용: 주문·신청·가입이 여러 API 주소(endpoint)를 순서대로 호출한다. Network와 JS의 route·함수 이름으로 전체 단계를 먼저 정리한다.

전제 — 정상 흐름:

1. POST /api/cart/add → 장바구니 추가
2. POST /api/order/preview → 주문 미리보기 (가격 계산)
3. POST /api/order/create → 주문 생성 (PENDING 상태)
4. POST /api/payment → 결제 (PENDING → COMPLETED)
5. POST /api/order/confirm → 주문 확정

비교 순서:

1. 정상 순서 전체 실행
2. 미리보기 생략 후 주문 생성
3. 결제 생략 후 주문 확정
4. 완료 API를 새 주문 ID로 직접 호출
5. 클라이언트가 보내는 결제 상태 필드 제거·변경

확인할 것: 마지막 API가 200을 반환하는지만 보지 않는다. 주문 상태, 결제 거래 ID, 상품·서비스 권한이 실제로 생성됐는지 확인한다.

3. 쿠폰·포인트 조건 우회​

이럴 때 사용: 쿠폰·할인 코드·포인트에 만료일, 대상 상품, 최소 금액, 사용자 등급, 사용 횟수 조건이 있다.

조건을 한 번에 하나씩 바꾼다.

만료된 테스트 쿠폰 적용
최소 주문 금액 바로 아래에서 적용
대상 상품이 아닌 테스트 상품에 적용
신규 사용자 조건을 기존 테스트 계정에서 적용
첫 구매 조건을 두 번째 테스트 주문에서 적용

순서 변경도 확인한다.

- 쿠폰 적용 후 상품 제거 → 최소 주문 금액 미달이지만 쿠폰 유지
- 쿠폰 적용 후 대상 상품을 비대상 상품으로 교체
- 포인트 적용 후 배송지 변경 → 배송비와 최종 금액 재계산

JS에서 배열 형태를 받는 단서가 있을 때만 중첩 사용을 확인한다.

{
"couponCodes": ["TEST10", "TEST20"]
}

확인할 것: 주문 상세의 실제 할인액, 쿠폰 사용 이력, 남은 포인트를 함께 비교한다. 화면 표시만 할인되고 결제 단계에서 정상 금액으로 다시 계산되면 취약으로 확정하지 않는다.

4. 환불·취소 후 정산과 권한​

이럴 때 사용: 환불·주문 취소·부분 환불 후 금액과 상품 사용 권한이 함께 정리되어야 한다.

환불 후 권한 확인:

1. 테스트 디지털 상품 구매
2. 환불 요청과 승인 완료
3. 강의·다운로드·구독 권한 다시 확인

부분 환불 합계 확인:

- 테스트 주문의 일부 금액 환불
- 남은 환불 가능 금액 조회
- 결제액을 넘는 두 번째 환불은 테스트 환경이나 되돌릴 수 있는 주문에서만 확인

JS나 Network에서 환불 금액을 클라이언트가 보내는 경우:

POST /api/refund HTTP/1.1
{
"orderId": "TEST-ORDER",
"amount": 1000
}

확인할 것: 환불 응답뿐 아니라 결제 원장, 환불 누적액, 주문 상태, 상품 권한을 함께 본다.

5. 가입 혜택·추천 조건​

이럴 때 사용: 가입 보너스·추천인 보상·첫 구매 혜택이 동일 사용자에게 한 번만 제공된다.

먼저 서비스가 동일 사용자를 어떤 값으로 판단하는지 확인한다.

이메일 주소
인증된 휴대폰 번호
결제 수단
본인확인 식별값
탈퇴 계정 이력

테스트 계정 범위에서 다음을 비교한다.

자기 추천 코드 입력
가입 후 추천 코드 변경·추가
탈퇴 후 재가입 시 혜택 상태
초대받은 계정이 조건을 충족하기 전 보상 지급 여부

확인할 것: 단순히 계정을 여러 개 만들 수 있다는 사실보다 동일 사용자 제한과 보상 조건이 실제로 우회되는지 확인한다.

6. 사용 횟수·기간·한도 우회​

이럴 때 사용: 1인 1회, 1일 N회, 최대 N개, 무료 체험 N일 같은 제한이 있다.

같은 계정에서 순차적으로 한도 초과 요청
삭제·취소 후 사용 횟수 복구 여부
탈퇴·재가입 후 무료 체험 복구 여부
선물·양도·다른 화면을 거친 보유 한도 우회
서버 기준 날짜 경계 전후의 사용 횟수

클라이언트의 Date Header나 화면 시간만 바꿔 성공했다고 판단하지 않는다. 서버가 기록한 사용 일자와 횟수를 확인한다. 동시 요청으로 제한을 넘기는 경우는 Race Condition으로 분리한다.

7. 상태 전이 위반​

이럴 때 사용: 주문·결제·배송·승인 객체가 명확한 상태값을 갖는다. JS의 상수와 응답 필드로 허용 전이표를 만든다.

시나리오:

[정상 전이]
PENDING → CONFIRMED → SHIPPED → DELIVERED
PENDING → CANCELLED

[우회 시도]
- CANCELLED → SHIPPED (취소된 주문 발송)
- DELIVERED → CANCELLED (배송 완료 후 취소 → 환불 + 상품 보유)
- SHIPPED → PENDING (역방향 전이)

확인할 것: 요청의 상태 필드만 바뀌는지, 서버 객체와 후속 처리까지 바뀌는지 확인한다. UI가 이전 화면으로 이동하는 것만으로 상태 전이 성공을 확정하지 않는다.

8. 통화·배송비·외부 연동 조건​

이럴 때 사용: 국가·통화·배송지·세금·외부 결제 결과에 따라 금액이나 상태가 달라진다.

표시 통화와 결제 통화가 같은지
배송지 변경 후 배송비·세금이 다시 계산되는지
결제 성공 callback의 거래 ID와 주문 ID가 서버에서 대조되는지
외부 결제 취소 후 내부 주문 상태가 함께 갱신되는지

환율이나 결제 결과를 임의 값으로 대량 시도하지 않는다. 테스트 주문 한 건에서 표시 금액, 결제 요청, 서버 주문 상세를 서로 대조한다.


우회 매트릭스​

관찰된 증상다음 시도확인할 것
변조 필드가 무시됨필드 제거와 JS 계산 함수 확인서버 재계산인지 다른 필드 사용인지
UI에서 다음 단계로 못 감JS route와 Network에서 다음 endpoint 확인직접 호출 시 이전 단계 검증
쿠폰 적용 API는 거부함적용 후 상품·배송지 변경최종 결제 단계에서 조건 재검증
상태 필드를 바꿔도 동일함상태별 전용 action endpoint 확인body 값이 아니라 서버 action으로 전이할 수 있음
응답은 성공이나 화면은 그대로임주문 상세·이력·잔액 재조회메시지와 실제 상태 분리
클라이언트 시간이 무시됨서버 기준 날짜 경계와 이력 확인시간대보다 횟수 계산 기준이 핵심
JS에 숨은 기능이 보임실제 역할별 세션으로 직접 요청오래된 코드와 활성 기능 구분
같은 요청을 순차 실행하면 막힘동시 요청에서만 달라지는지 분리Race Condition 문서로 이동

취약 판정 기준​

취약 확정​

  • 서버가 클라이언트의 가격·할인·정산값을 신뢰해 실제 주문·결제 금액이 달라진다.
  • 중간 단계를 생략하거나 역순으로 호출해 정상 흐름에서는 불가능한 최종 상태에 도달한다.
  • 쿠폰·포인트·혜택의 대상·기간·횟수 조건을 우회해 실제 이익이 적용된다.
  • 환불 합계가 결제액을 넘거나 환불 뒤에도 유료 상품·서비스 권한이 유지된다.
  • 허용되지 않은 상태 전이가 서버 객체와 후속 처리에 반영된다.
  • 동일 사용자 제한을 우회해 가입·추천·무료 체험 혜택을 반복 수령한다.

후보 / 보류​

  • JS에 가격·상태·숨은 endpoint 단서가 있지만 서버에서 처리되지 않는다.
  • 응답에는 성공이 표시되지만 주문 상세·잔액·권한 상태는 정상이다.
  • 프로모션 중첩·환불 후 일정 기간 접근처럼 의도된 정책인지 확인되지 않았다.
  • 테스트 데이터 복원이 불가능해 금전·외부 연동의 최종 단계까지 확인하지 못했다.

영향 상승 조건​

  • 실제 결제액·환불액·포인트·재고에 반복 가능한 불일치가 생긴다.
  • 다른 사용자의 주문·보상·서비스 제공에 영향을 준다.
  • 일반 사용자도 숨은 관리자·승인 흐름을 실행할 수 있다.
  • 하나의 결함으로 여러 주문이나 혜택에 같은 결과를 반복 적용할 수 있다.

참고자료​

공식 및 테스트 가이드​

커뮤니티 참고​