13 KiB
KVE-2026-2424 LG 가상계좌 입금 통보 위조 차단
shop/settle_lg_common.php가 빈 LG 상점키로 만든 통보를 받아 다른 PG의 미입금 주문을 입금 처리할 수 있던 경로를 차단한다. PC·모바일의 가상계좌 통보는 이 공통 URL을 사용한다. 주문 처리 로직은 lib/lg_notification.lib.php로 분리했다.
적용 절차
- 파일·DB를 백업하고 신규 주문·결제 접수를 일시 중지한다. 변경 파일 반영과 함께 최고관리자의 환경설정 → DB업그레이드에서
20260918_001_lg_notification을 적용한다. 새 설치 SQL에도 같은 컬럼과 이력 테이블이 포함된다. 일반 요청에서 DDL을 실행하지 않으며, 스키마 적용 전에는 새 주문 저장이나 통보 처리가 실패할 수 있다. - DB 계정의
LOCK TABLES및 관련 주문·개인결제·장바구니·통보 이력 테이블의SELECT·INSERT·UPDATE권한을 확인한다. - 기존 미입금 LG 거래를 원장과 대조한다.
od_lg_mid·pp_lg_mid는 기존 행에 빈값으로 추가되며, 이 상태에서는 통보를 거부한다. 현재 설정을 과거 거래 전체에 일괄 복사하지 않는다. PG 거래 내역의 주문번호·거래번호·상점 MID·운영/테스트 여부·계좌·청구액·입금 여부를 확인한 건만 담당자가 해당 MID를 보정하고 PG 재통보를 요청한다. 운영 MID는si_접두어, 테스트 MID는tsi_접두어까지 포함한 실제 값이다. 이미 입금된 기존 거래에는 처리 이력을 임의로 만들지 말고 수동 대조한다. - 웹서버에서 HTTPS 및 통보 발신지 처리를 확인한다. 애플리케이션은
REMOTE_ADDR만 신뢰한다. 프록시를 사용하는 경우 관리자가 신뢰 프록시 범위를 제한하고 원본 IP를 웹서버에서 복원해야 한다.X-Forwarded-For등을 애플리케이션에서 무조건 신뢰하거나 프록시 IP를 PG IP 목록에 추가해서는 안 된다. - 실제 계약된 XPay 테스트 환경에서 PC·모바일·일반/개인결제의 계좌 발급, 입금, 은행 입금 취소 및 재통보를 확인한 뒤 접수를 재개한다. 특히 계약별 취소 통보의 금액·순번 형식은 아래 지원 범위와 대조한다.
- 실패 통보, 미완료 처리 이력과 출고 후 은행 취소를 감시한다. 현재 설정의 MID/키가 과거 계약과 달라진 거래는 자동 처리하지 않는다. PG 전환만 있었고 LG 인증정보와 저장 거래를 검증할 수 있는 경우에는 기존 LG 거래를 처리한다.
신고된 위조 통보가 실제로 있었는지는 별도 확인 사항이다. 과거 LG 통보 기록과 입금 원장·주문 상태를 대조하고, 코드 적용만으로 과거 주문 정합성까지 복구됐다고 판단하지 않는다.
인증과 거래 검증
- 공식 XPay 공통사항 6.2의 운영·테스트 공통 통보 IP 10개에서 온 POST만 받는다. 임의의 클라이언트 IP, 관리자 세션, 전달 헤더는 인증 근거가 아니다.
- 설정 MID·상점키가 없으면 거부한다. 요청 MID는 설정에서 계산한
si_/tsi_MID 중 하나여야 한다. 계약에서 확인되지 않은 임의의 키 길이 조건은 추가하지 않는다. - XPay 응답 규격의 MD5를 동일 길이 상수 시간 비교로 검증한다. MD5 입력에는 입금 플래그·거래번호·통보 순번이 포함되지 않으므로 해시 검사만으로 전체 요청의 무결성이 보장된다고 간주하지 않는다. PG 발신지와 서버 저장 거래 검증을 함께 적용한다.
- 필수 입력의 문자열 여부·길이·허용 문자, 실제 달력 날짜와 시각, 양의 청구액 및 DB signed INT 범위를 검사한다. 배열·빈 TID·지수 표기·음수·0원·소수·범위 초과는 거부한다.
- 결제 당시 서버에서 구성한
$LGD_MID를 PC·모바일 일반 주문과 개인결제에 저장한다. 현재 활성 PG나 테스트 설정으로 과거 주문을 판단하지 않는다. 통보 MID와 저장 MID, PG=lg, 결제수단=가상계좌, 거래번호·계좌를 대조하며 연결 주문의 테스트 여부도 검사한다. - 일반/개인결제 번호가 충돌하거나 연결 주문이 없으면 거부한다. 개인결제에 연결된 주문의 원래 PG는 다를 수 있으므로 개인결제 자체의 LG 거래를 검사한다.
- 일반 입금은 서버의 상품·배송비·쿠폰·포인트·취소·환불 계산과 저장 미수금이 일치해야 한다. 미입금 주문의 청구액 전액만 반영한다. 개인결제는
pp_price전액을 반영하며 연결 주문 미수금을 초과할 수 없다.
지원하는 통보와 재처리
기본 통합결제창의 일반형 가상계좌 전액 입금을 대상으로 한다. 고정식 계좌나 부분입금 기능을 새로 지원하는 패치는 아니다.
| 플래그 | 금액 검증 및 동작 |
|---|---|
R |
누적/현입금액 0. 저장된 거래와 대조한 뒤 수신만 확인하며 입금액을 바꾸지 않는다. |
I |
LGD_AMOUNT, LGD_CASTAMOUNT, LGD_CASCAMOUNT가 청구액 전액과 같아야 한다. 해당 거래가 미입금 상태인 경우에만 반영한다. |
C |
누적액 0, 현입금액 0 또는 청구액 전액인 은행 취소를 처리한다. 확인된 입금 이력이 있으면 해당 금액만 차감한다. 입금보다 먼저 도착하면 취소 이력을 남겨 지연 입금 통보를 차단한다. |
통보 이력은 MID·TID·주문번호·숫자로 정규화한 LGD_CASSEQNO·플래그로 구별한다. 같은 이벤트의 금액·계좌 등 업무 값이 다르면 거부한다. 완료된 이벤트 재전송은 금액을 다시 반영하지 않고 OK로 응답한다. 타임스탬프의 임의 만료 시간으로 수동 재통보를 막지 않는다.
순번은 입금 순서를 나타내는 숫자(최대 6자리)로 취급한다. 같은 순번의 입금보다 취소가 우선하며, 더 낮은 순번의 지연 통보는 업무 데이터를 변경하지 않는다. 취소 후 새 입금은 증가한 순번에서만 허용한다. 계약 환경이 순번을 재사용하거나 다른 취소 금액 형식을 사용하면 원장과 대조하여 연동 정책을 확정해야 하며, 검증을 제거해 우회하지 않는다. 실제 PG 통보의 모든 변형을 확인한 것은 아니다.
은행 취소 시 입금액·미수금을 복원하고 입금·준비 상태를 주문으로 되돌린다. 이미 배송·완료인 경우 물류 이력은 유지하고 미수금 및 운영자 대조 필요 로그를 남긴다. 출고 중단·회수·디지털 상품 접근·포인트 회수는 운영자가 별도로 조치해야 한다. 이력 없이 입금액이 남아 있는 과거 거래, 취소·반품 주문, 부분입금과 불일치 금액은 자동으로 추정·차감하지 않는다.
개인결제의 은행 취소는 활성 입금 이력에 기록된 연결 주문과 현재 연결 주문을 대조한다. 입금 후 연결 주문 변경·해제·추가가 있으면 금액과 이력을 변경하지 않고 FAIL 및 연결 충돌 로그를 남긴다. 다른 주문에서 대신 차감하지 않는다. 담당자는 PG 원장과 입금 이력을 대조하고 원래 연결 관계를 복원한 뒤 동일 취소 통보를 재전송한다. 개인결제를 사용 안 함으로 바꿔도 기존 입금의 은행 취소는 처리하며, 사용 중지 상태의 신규 입금은 계속 거부한다.
중간 실패와 복구
MyISAM·InnoDB 모두 관련 네 테이블의 쓰기 잠금 안에서 검증·처리한다. ln_plan에 행별 변경 전 값과 목표값을 기록한 다음 개인결제·장바구니·주문을 갱신한다. 재시도에서는 가산 연산 대신 같은 목표값을 저장하므로 개인결제의 연결 주문 금액이 중복 가산되지 않는다. 모든 결과를 다시 읽어 확인하고 ln_done=1로 기록해야 OK로 응답한다. 실패 응답은 FAIL이다.
이 방식이 MyISAM 여러 테이블을 원자적 트랜잭션으로 바꾸지는 않는다. 중간 실패 시 일부 값이 반영될 수 있고, 같은 통보 재전송으로 나머지를 완료한다. 현재 행이 기록된 변경 전/후 어느 쪽과도 맞지 않으면 외부 변경으로 보고 복구를 중단한다. 미완료 이벤트가 남은 거래 및 연결 주문의 다른 신규 LG 통보도 차단한다. 다른 PG나 관리자 작업에서 발생한 충돌은 원장 대조가 필요하다. 테이블 전체 잠금 동안 주문 및 관리자 쿼리가 잠시 대기할 수 있다.
복구 점검 SQL(실제 설치의 테이블 접두어로 변경):
SELECT ln_key, ln_trade, ln_seq, ln_flag, od_id, ln_created_at
FROM g5_shop_lg_noti WHERE ln_done = 0 ORDER BY ln_created_at;
미완료 이력을 삭제하거나 ln_done만 1로 변경하지 않는다. PG 원장과 주문·개인결제·장바구니·ln_plan을 대조하고 충돌 원인을 해결한 뒤 동일 통보를 재전송한다. 공식 가이드는 자동 재전송 후에도 처리되지 않은 거래를 구 상점관리자의 전송실패내역에서 재전송할 수 있다고 안내한다. 이력에는 계좌·금액 등 거래 정보가 포함되므로 주문 데이터와 같은 접근·백업 보호를 적용한다.
검증
python3 tests/lg_notification_http.py 격리_MySQL_포트
php tests/migration_line_endings.php
php tests/migration_install_history.php 격리_MySQL_소켓
HTTP 검사는 localhost 지정 포트의 root/빈 비밀번호인 별도 테스트 MySQL에 고유 임시 DB를 만들고 삭제한다. 실제 콜백 파일과 SQL을 실행하되 공통 초기화·PG 발신 IP·SQL 실패 주입만 대역으로 사용한다. Python 3, PHP CLI/mysqli, MySQL CLI가 필요하다. 테스트 파일은 일반 배포 아카이브에서 제외된다.
2026-09-18: PHP 8.4.22·MySQL 8.0.46의 MyISAM/InnoDB에서 HTTP·DB 645건 통과. 빈 키 제보 조건, 서명/IP/상점/거래/금액 거부, 일반·개인결제, 동시·중복·역순 통보, 은행 취소, 각 쓰기 전 실패·반영 후 응답 유실·실제 SQL 오류와 복구, 외부 수정 충돌, 스키마 누락, 새 설치/기존 업그레이드·재실행·쇼핑몰 미설치 분기를 확인했다. 마이그레이션 줄바꿈 13건 및 기존 신규 설치 이력·체크섬·재실행 검사가 통과했다. 변경 PHP 8개 파일의 구문 검사는 PHP 5.2.17·8.4.22에서 통과했다.
실제 PG 승인·은행 입금, 전체 주문서 제출, 프록시 설정, MySQL 5.0 실행, 실제 운영 배포는 이 격리 검증에 포함하지 않는다. 자동 거래조회 API는 이번 패치에 추가하지 않았다. 공식 IP·서명과 저장 거래를 함께 검증하고, 불명확한 기존 거래는 원장 대조로 처리한다.
2026-09-18 추가 보완 검증: PHP 8.4.25·별도 localhost MySQL 8.0.46에서 MyISAM/InnoDB 회귀 검사 715건과 실제 설치의 공통 초기화·DB 함수를 사용하는 검사 391건을 통과했다. 입금 후 개인결제 연결 주문 변경·해제·추가의 차단 및 원래 연결 복원 후 재통보, 사용 중지 후 동시 은행 취소·중복 차감 방지·신규 입금 거부를 추가했다. Windows 실행에 맞춰 테스트 사본의 경로·프로세스 종료를 조정하고 PHP 서버 4개로 동시 요청을 처리했다. 실제 초기화 검사에서도 PG 발신 IP는 로컬 라우터로 대입했으며 실제 PG 통신을 수행한 것은 아니다. 두 검사 집합의 시나리오는 중복되며, 이번 보완은 추가 DB 마이그레이션을 요구하지 않는다.
배포 및 인터넷진흥원 회신
현재는 코드 수정과 격리 검증을 완료한 상태이다. 공개 배포 버전·일정, 실제 계약 PG 연동 검사, 운영 반영과 KISA 제출은 별도 기록한다.
KVE-2026-2424와 관련하여 미설정 LG 인증정보 차단, 통보 발신지·서명·저장 거래·결제금액 검증을 보완하였습니다. 일반 주문과 개인결제에 중복 통보 방지 및 은행 입금 취소 처리를 적용하고, DB 처리 중 실패한 건을 동일 통보로 복구하도록 수정하였습니다. 격리 환경에서 위조 요청 차단, 동시·역순 통보, 중간 실패 복구 및 신규/기존 DB 적용을 검증하였습니다. 기존 미입금 거래는 PG 원장 대조 후 결제 당시 MID를 확인해야 하며, 실제 PG 연동 검증과 공개 배포 버전·일정은 확정 후 별도로 통보하겠습니다.
규격 근거
2026-09-18에 공식 구 모듈 가이드에서 연결되는 다음 문서를 확인했다.
- XPay 결제모듈 공통사항 v1.0.8: 6.2의 가상계좌 입금 통보 발신지. 현행 방화벽 안내의 인바운드 IP와도 일치한다.
- PC 통합결제창 가상계좌 v1.0.3: 일반형 계좌의 일회성 입금, 발급/입금/은행 취소 구분, 통보 응답 및 재전송 안내.
- 저장소의
plugin/lgxpay/lgdacom/XPayClient.php::GetHashData2()와 기존 콜백의 응답 해시 계산식도 대조했다. 키 길이·자동 조회 API를 임의로 가정하지 않았다.