security: KVE-2026-2345 실제 설치 환경 검증 결과 기록
PHP·MySQL 실제 설치 환경의 수정 전 재현과 수정 후 차단, 정상 주문 저장 등 46개 시나리오 결과를 기록한다. 실제 PG와 브라우저 UI 등 미검증 범위를 구분한다.
This commit is contained in:
@@ -43,3 +43,12 @@ PHP의 확장 경로 설정이 필요한 환경에서는 `-d extension_dir=/path
|
||||
기존 행 병합 결과는 모든 상품에 대해 변경 전에 검사한다. 모바일 이니시스는 승인 전에 장바구니를 검사하고, 승인 후 주문 검증 실패 시 서버 세션과 해시로 확인한 거래번호를 기존 INIAPI 취소 함수에 전달한다. 취소 응답이 성공일 때만 취소 완료를 안내하며, 실패는 로그와 확인 필요 안내를 남긴다.
|
||||
|
||||
이 검증의 SQL은 SQLite에서 실행하므로 MyISAM·InnoDB 테스트를 대체하지 않는다. PG 승인 응답과 취소 API는 모의 구현이며 실제 PG의 취소 성공이나 전체 주문 저장 완료를 검증한 결과가 아니다. 승인 이후 검사와 저장 사이의 동시 변경, 통신 실패 후 운영 복구, 실제 PG 통합 검증은 별도로 필요하다.
|
||||
|
||||
## 실제 설치 환경 검증 기록
|
||||
|
||||
2026-09-10에 PHP 8.4.25와 MySQL 8.0.46의 별도 로컬 프로세스로 수정 전·후 영카트를 실제 설치하여 HTTP 요청부터 무통장 주문 저장까지 확인했다. 공통 초기화·인증·주문 코드를 대체하거나 중간에서 종료하지 않았다. 이 실행은 위 단위/모의 테스트와 별도의 검증이다.
|
||||
|
||||
- [검증 환경·46개 시나리오·판정과 한계](reports/KVE-2026-2345-installed-verification.md)
|
||||
- [시나리오별 결과 JSON](reports/KVE-2026-2345-integration-results.json)
|
||||
|
||||
보고서는 해당 커밋과 환경에서 수행한 검증 기록이며 자동 재실행 도구가 아니다. DB 접속 설정, 비밀번호, 세션 파일과 테스트 DB는 저장소에 포함하지 않는다. 실제 PG 승인·취소와 브라우저 JavaScript, 동시성, 쿠폰·포인트 등 전체 정산은 이 검증에 포함되지 않는다.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# KVE-2026-2345 실제 설치 환경 검증
|
||||
|
||||
검증일: 2026-09-10
|
||||
|
||||
## 환경과 수행 방식
|
||||
|
||||
Docker Desktop 시작을 시도했으나 WSL 2 커널이 없어 실패했다. 따라서 이번 환경은 가상머신/컨테이너가 아니라 Windows의 작업 전용 디렉터리와 로컬 전용 포트에 구성한 별도 프로세스 환경이다. 운영 시스템 수준의 샌드박스는 아니다.
|
||||
|
||||
- PHP 8.4.25 내장 HTTP 서버, 실제 MySQL Community Server 8.0.46.
|
||||
- MySQL과 HTTP 모두 `127.0.0.1`에만 바인딩. 시스템 서비스 설치 없음.
|
||||
- 수정 전 `1c067f831`, 수정 후 `f7ba144b2e74e55106238c61c76367d2b84bea1a` 소스를 각각 별도 디렉터리에 전개.
|
||||
- 수정 전·후를 서로 다른 테스트 DB에 영카트의 실제 `install/install_db.php` HTTP 요청으로 설치. 전체 설치 테이블과 `data/dbconfig.php` 생성 확인.
|
||||
- 상품·테스트 회원 등 시험 데이터는 테스트 DB에 SQL로 준비. 회원 검증은 일반 구매자 레벨 2 계정으로 실제 `bbs/login_check.php` 로그인 수행.
|
||||
- HTTP 쿠키를 유지하면서 실제 `_common.php`, 인증·세션 처리, 상품·장바구니·주문 처리 코드를 실행. 처리 함수를 모의 함수로 교체하지 않았고 주문 코드를 중간에서 자르지 않았다.
|
||||
- PC·모바일 User-Agent를 사용해 서버의 분기와 각 주문 제출 주소를 실행. 브라우저의 JavaScript·화면 클릭 자동화는 수행하지 않았다.
|
||||
- 무통장 결제만 사용. 주문서 HTML에서 입력값을 가져와 주문 제출 후 실제 주문 테이블의 상태·가격·미수금·회원 ID를 확인.
|
||||
- 테스트 메일 발송 비활성화. 실계좌 송금, 외부 PG 승인·취소, 실제 운영 주문은 없음.
|
||||
- MyISAM·InnoDB 비교는 상품·옵션·장바구니·주문 4개 테이블의 엔진을 전환하여 수행. 전체 설치 테이블을 InnoDB로 전환한 시험은 아님.
|
||||
|
||||
## 결과
|
||||
|
||||
| 검증 | 조합 수 | 실제 DB/HTTP 결과 |
|
||||
|---|---:|---|
|
||||
| 수정 전 취약점 재현 | 8 | 유료 50,000원 상품의 장바구니가 `ct_price=50000, io_type=1, io_price=0, ct_qty=1`로 저장됨. 최종 무통장 주문은 `입금`, 상품대금·미수금·입금액 모두 0원으로 저장됨 |
|
||||
| 수정 후 조작 차단 및 정상 주문 | 24 | 조작 요청의 장바구니 저장 차단. 정상 요청은 50,000원, 미수금 50,000원, `주문` 상태로 최종 DB 저장. 회원 ID도 로그인 여부와 일치 |
|
||||
| 정상 무료상품 | 4 | 상품 가격 0원인 정상 주문을 0원·`입금`으로 저장 가능 |
|
||||
| 패치 전 조작 장바구니 | 4 | 테스트 DB에 기존 조작 상태를 구성한 후 주문 준비와 PC·모바일 주문 제출 모두 거부. 새 주문 없음 |
|
||||
| 옵션 가격 변경 후 병합 실패 | 6 | 기존 보조옵션 가격 1,000원을 현재 가격 1,500원으로 변경하고 재추가 시, 오류가 발생해도 모든 기존 행의 수량·가격이 그대로 유지됨 |
|
||||
|
||||
총 46개 시나리오 조합이다. 수정 후 24개 조합은 각각 조작 요청과 정상 구매를 함께 검증했다. 기존 단위/모의 테스트 71개와 서로 다른 검증이며 수를 혼합하지 않는다.
|
||||
|
||||
- 수정 전 8개: DB 엔진 2 × PC/모바일 2 × 공통/기본 테마 AJAX 2. 비회원 조건.
|
||||
- 수정 후 24개: DB 엔진 2 × 회원/비회원 2 × PC/모바일 2 × 공통 AJAX/기본 테마 AJAX/일반 추가 3.
|
||||
- 무료상품·기존 조작 장바구니: 각각 DB 엔진 2 × PC/모바일 2.
|
||||
- 병합 실패: DB 엔진 2 × 장바구니 추가 경로 3.
|
||||
|
||||
HTTP 서버 로그에서 PHP Fatal error는 없었다. 최초 탐색 요청에 `sw_direct` 필드를 생략했을 때 수정 후 AJAX 파일에서 해당 변수 미정의 Warning 4건이 기록되었다. 이후 명시적으로 `sw_direct=0`을 전송한 시험 요청에서는 추가 Warning이 발생하지 않았다. 이 경고는 이번 작업에서 수정하지 않았다.
|
||||
|
||||
## 판정과 한계
|
||||
|
||||
**신고된 핵심 0원 주문 문제는 실제 설치·HTTP·MySQL·최종 주문 저장까지 수정 전 재현과 수정 후 차단을 확인했다.** 후속 P2인 장바구니 부분 병합 문제도 실제 MySQL 환경에서 기존 행 보존을 확인했다.
|
||||
|
||||
이번 검증이 실제로 확장한 범위는 전체 애플리케이션 부트스트랩, 일반 회원 로그인, 세션, HTTP 요청, 실제 MySQL SQL, 무통장 주문 저장이다. 기존 CLI/SQLite/모의 PG 검증만 반복한 결과가 아니다.
|
||||
|
||||
다음 항목은 여전히 미검증이다.
|
||||
|
||||
- 실제 PG 테스트 상점을 이용한 승인·취소 및 실패 복구. 따라서 후속 P1의 실제 외부 취소 성공은 이전 모의 검증 수준이며, 이번 결과로 실 PG 검증 완료라고 표현하지 않는다.
|
||||
- 브라우저 JavaScript·버튼·렌더링, 커스텀 테마와 다른 PHP/OS/웹서버 환경.
|
||||
- 쿠폰·포인트·유료 배송비·과세/면세 조합의 전체 정산.
|
||||
- 동시 요청·상품 변경 경쟁·DB 장애·통신 장애와 재시도.
|
||||
- 전체 영향 버전, 배포 버전·일정, 운영 배포, KISA 제출.
|
||||
|
||||
따라서 이슈 #42의 전체 완료 조건 충족이나 운영 배포 승인으로 간주하지 않는다. 실제 PG 검증은 테스트 상점 정보와 테스트 결제 절차가 필요하다.
|
||||
|
||||
이번 검증에서는 저장소 코드를 변경하지 않았다. 테스트 서버는 검증 후 종료하고 시험 결과와 로컬 테스트 자료만 보존한다.
|
||||
@@ -0,0 +1,374 @@
|
||||
{
|
||||
"MyISAM": [
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
}
|
||||
],
|
||||
"InnoDB": [
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": false,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": false,
|
||||
"mobile": true,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": false,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"member": true,
|
||||
"mobile": true,
|
||||
"route": "shop/cartupdate.php",
|
||||
"attack": "blocked",
|
||||
"normal_order": "saved",
|
||||
"amount": 50000
|
||||
}
|
||||
],
|
||||
"before": [
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": false,
|
||||
"route": "shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": false,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": true,
|
||||
"route": "shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": true,
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"status": "입금",
|
||||
"cart_price": 0,
|
||||
"paid": 0
|
||||
}
|
||||
],
|
||||
"extra": [
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": false,
|
||||
"case": "legitimate_free_order",
|
||||
"result": "saved"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": false,
|
||||
"case": "prepatch_corrupt_cart",
|
||||
"result": "preparation_and_submission_blocked"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": true,
|
||||
"case": "legitimate_free_order",
|
||||
"result": "saved"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"mobile": true,
|
||||
"case": "prepatch_corrupt_cart",
|
||||
"result": "preparation_and_submission_blocked"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"route": "shop/ajax.action.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
},
|
||||
{
|
||||
"engine": "MyISAM",
|
||||
"route": "shop/cartupdate.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": false,
|
||||
"case": "legitimate_free_order",
|
||||
"result": "saved"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": false,
|
||||
"case": "prepatch_corrupt_cart",
|
||||
"result": "preparation_and_submission_blocked"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": true,
|
||||
"case": "legitimate_free_order",
|
||||
"result": "saved"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"mobile": true,
|
||||
"case": "prepatch_corrupt_cart",
|
||||
"result": "preparation_and_submission_blocked"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"route": "shop/ajax.action.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"route": "theme/basic/shop/ajax.action.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
},
|
||||
{
|
||||
"engine": "InnoDB",
|
||||
"route": "shop/cartupdate.php",
|
||||
"case": "stale_addon_merge",
|
||||
"result": "all_rows_unchanged"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user