security: KVE-2026-2346 KCP 통보 검증과 입금·망취소 재처리 보강

테스트·운영 발신지와 결제 당시 상점·거래·계좌·금액을 대조하여 위조 입금 처리를 차단한다. 개인결제와 연결 주문의 중복 가산을 막고 통보 이력과 목표값으로 MyISAM/InnoDB 중간 실패를 재처리한다.

DB 마이그레이션과 기존 거래 적용·복구 안내를 추가했다. 격리 HTTP/MySQL 검사 420개, PHP 문법 검사와 설치 스키마 대조가 통과했다. 실제 KCP 연동 확인과 배포·KISA 회신은 별도 진행이 필요하다.
This commit is contained in:
whitedot
2026-09-10 05:17:59 +00:00
parent 560aa0af47
commit 7a80ae913d
12 changed files with 650 additions and 267 deletions
+4
View File
@@ -531,6 +531,9 @@ else
}
$od_pg = $default['de_pg_service'];
// KCP 통보는 결제 당시 사용한 상점코드와 대조한다.
$od_kcp_site_cd = ($od_pg === 'kcp' && isset($g_conf_site_cd))
? sql_escape_string($g_conf_site_cd) : '';
$tno = isset($tno) ? $tno : '';
$od_receipt_time = isset($od_receipt_time) ? $od_receipt_time : '';
@@ -630,6 +633,7 @@ $sql = " insert {$g5['g5_shop_order_table']}
od_receipt_time = '$od_receipt_time',
od_misu = '$od_misu',
od_pg = '$od_pg',
od_kcp_site_cd = '$od_kcp_site_cd',
od_tno = '$od_tno',
od_app_no = '$od_app_no',
od_escrow = '$od_escrow',
+4
View File
@@ -192,6 +192,9 @@ if((int)$pp['pp_price'] !== (int)$pg_price) {
}
$pp_pg = $default['de_pg_service'];
// KCP 통보는 결제 당시 사용한 상점코드와 대조한다.
$pp_kcp_site_cd = ($pp_pg === 'kcp' && isset($g_conf_site_cd))
? sql_escape_string($g_conf_site_cd) : '';
$pp_email = get_email_address($pp_email);
// 결제정보 입력
@@ -199,6 +202,7 @@ $sql = " update {$g5['g5_shop_personalpay_table']}
set pp_email = '$pp_email',
pp_hp = '$pp_hp',
pp_pg = '$pp_pg',
pp_kcp_site_cd = '$pp_kcp_site_cd',
pp_tno = '$pp_tno',
pp_app_no = '$app_no',
pp_receipt_price = '$pp_receipt_price',
+4 -267
View File
@@ -1,271 +1,8 @@
<?php
include_once('./_common.php');
include_once(G5_LIB_PATH.'/etc.lib.php');
include_once(G5_LIB_PATH.'/mailer.lib.php');
include_once(G5_LIB_PATH.'/kcp_notification.lib.php');
/*------------------------------------------------------------------------------
※ KCP 에서 가맹점의 결과처리 페이지로 데이터를 전송할 때에, 아래와 같은
IP 에서 전송을 합니다. 따라서 가맹점측께서 전송받는 데이터에 대해 KCP
에서 전송된 건이 맞는지 체크하는 부분을 구현할 때에, 아래의 IP 에 대해
REMOTE ADDRESS 체크를 하여, 아래의 IP 이외의 다른 경로를 통해서 전송된
데이터에 대해서는 결과처리를 하지 마시기 바랍니다.
------------------------------------------------------------------------------*/
if(!$default['de_card_test']) {
switch ($_SERVER['REMOTE_ADDR']) {
case '203.238.36.58' :
case '203.238.36.160' :
case '203.238.36.161' :
case '203.238.36.173' :
case '203.238.36.178' :
case '103.215.144.173' : //판교 IDC IP주소 2019년 4월 3일 추가
case '103.215.144.174' : //판교 IDC IP주소 2019년 4월 3일 추가
case '103.215.145.30' : //판교 IDC IP주소 2019년 4월 3일 추가
break;
default :
$super_admin = get_admin('super');
$egpcs_str = "ENV[" . serialize($_ENV) . "] "
. "GET[" . serialize($_GET) . "]"
. "POST[" . serialize($_POST) . "]"
. "COOKIE[" . serialize($_COOKIE) . "]"
. "SESSION[" . serialize($_SESSION) . "]";
mailer('경고', 'waring', $super_admin['mb_email'], '올바르지 않은 접속 보고', "{$_SERVER['SCRIPT_NAME']} 에 {$_SERVER['REMOTE_ADDR']} 이 ".G5_TIME_YMDHIS." 에 접속을 시도하였습니다.\n\n" . $egpcs_str, 2);
exit;
}
}
/* ============================================================================== */
/* = PAGE : 공통 통보 PAGE = */
/* = -------------------------------------------------------------------------- = */
/* = Copyright (c) 2006 KCP Inc. All Rights Reserverd. = */
/* ============================================================================== */
$data = kcp_noti_request($_POST, $_SERVER, $default);
$result = $data !== false && kcp_noti_process($data) ? '0000' : '9999';
?>
<?php
/* ============================================================================== */
/* = 01. 공통 통보 페이지 설명(필독!!) = */
/* = -------------------------------------------------------------------------- = */
/* = 에스크로 서비스의 경우, 가상계좌 입금 통보 데이터와 가상계좌 환불 = */
/* = 통보 데이터, 구매확인/구매취소 통보 데이터, 배송시작 통보 데이터 등을 = */
/* = KCP 를 통해 별도로 통보 받을 수 있습니다. 이러한 통보 데이터를 받기 = */
/* = 위해 가맹점측은 결과를 전송받는 페이지를 마련해 놓아야 합니다. = */
/* = 현재의 페이지를 업체에 맞게 수정하신 후, KCP 관리자 페이지에 등록해 = */
/* = 주시기 바랍니다. 등록 방법은 연동 매뉴얼을 참고하시기 바랍니다. = */
/* ============================================================================== */
//write_log("$g5[path]/data/log/kcp_common.log", print_r($_POST));
/* ============================================================================== */
/* = 02. 공통 통보 데이터 받기 = */
/* = -------------------------------------------------------------------------- = */
$site_cd = isset($_POST["site_cd"]) ? $_POST["site_cd"] : ''; // 사이트 코드
$tno = isset($_POST["tno"]) ? $_POST["tno"] : ''; // KCP 거래번호
$order_no = isset($_POST["order_no"]) ? $_POST["order_no"] : ''; // 주문번호
$tx_cd = isset($_POST["tx_cd"]) ? $_POST["tx_cd"] : ''; // 업무처리 구분 코드
$tx_tm = isset($_POST["tx_tm"]) ? $_POST["tx_tm"] : ''; // 업무처리 완료 시간
/* = -------------------------------------------------------------------------- = */
$ipgm_name = ""; // 주문자명
$remitter = ""; // 입금자명
$ipgm_mnyx = ""; // 입금 금액
$bank_code = ""; // 은행코드
$account = ""; // 가상계좌 입금계좌번호
$op_cd = ""; // 처리구분 코드
$noti_id = ""; // 통보 아이디
/* = -------------------------------------------------------------------------- = */
$refund_nm = ""; // 환불계좌주명
$refund_mny = ""; // 환불금액
$bank_code = ""; // 은행코드
/* = -------------------------------------------------------------------------- = */
$st_cd = ""; // 구매확인 코드
$can_msg = ""; // 구매취소 사유
/* = -------------------------------------------------------------------------- = */
$waybill_no = ""; // 운송장 번호
$waybill_corp = ""; // 택배 업체명
/* = -------------------------------------------------------------------------- = */
/* = 02-1. 가상계좌 입금 통보 데이터 받기 = */
/* = -------------------------------------------------------------------------- = */
if ( $tx_cd == "TX00" )
{
$ipgm_name = isset($_POST["ipgm_name"]) ? $_POST["ipgm_name"] : ''; // 주문자명
$remitter = isset($_POST["remitter"]) ? $_POST["remitter"] : ''; // 입금자명
$ipgm_mnyx = isset($_POST["ipgm_mnyx"]) ? $_POST["ipgm_mnyx"] : ''; // 입금 금액
$bank_code = isset($_POST["bank_code"]) ? $_POST["bank_code"] : ''; // 은행코드
$account = isset($_POST["account"]) ? $_POST["account"] : ''; // 가상계좌 입금계좌번호
$op_cd = isset($_POST["op_cd"]) ? $_POST["op_cd"] : ''; // 처리구분 코드
$noti_id = isset($_POST["noti_id"]) ? $_POST["noti_id"] : ''; // 통보 아이디
}
/* = -------------------------------------------------------------------------- = */
/* = 02-2. 가상계좌 환불 통보 데이터 받기 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX01" )
{
$refund_nm = isset($_POST["refund_nm"]) ? $_POST["refund_nm"] : ''; // 환불계좌주명
$refund_mny = isset($_POST["refund_mny"]) ? $_POST["refund_mny"] : ''; // 환불금액
$bank_code = isset($_POST["bank_code"]) ? $_POST["bank_code"] : ''; // 은행코드
}
/* = -------------------------------------------------------------------------- = */
/* = 02-3. 구매확인/구매취소 통보 데이터 받기 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX02" )
{
$st_cd = isset($_POST["st_cd"]) ? $_POST["st_cd"] : ''; // 구매확인 코드
if ( $st_cd == "N" ) // 구매확인 상태가 구매취소인 경우
{
$can_msg = isset($_POST["can_msg"]) ? $_POST["can_msg"] : ''; // 구매취소 사유
}
}
/* = -------------------------------------------------------------------------- = */
/* = 02-4. 배송시작 통보 데이터 받기 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX03" )
{
$waybill_no = isset($_POST["waybill_no"]) ? $_POST["waybill_no"] : ''; // 운송장 번호
$waybill_corp = isset($_POST["waybill_corp"]) ? $_POST["waybill_corp"] : ''; // 택배 업체명
}
/* ============================================================================== */
/* ============================================================================== */
/* = 03. 공통 통보 결과를 업체 자체적으로 DB 처리 작업하시는 부분입니다. = */
/* = -------------------------------------------------------------------------- = */
/* = 통보 결과를 DB 작업 하는 과정에서 정상적으로 통보된 건에 대해 DB 작업을 = */
/* = 실패하여 DB update 가 완료되지 않은 경우, 결과를 재통보 받을 수 있는 = */
/* = 프로세스가 구성되어 있습니다. 소스에서 result 라는 Form 값을 생성 하신 = */
/* = 후, DB 작업이 성공 한 경우, result 의 값을 "0000" 로 세팅해 주시고, = */
/* = DB 작업이 실패 한 경우, result 의 값을 "0000" 이외의 값으로 세팅해 주시 = */
/* = 기 바랍니다. result 값이 "0000" 이 아닌 경우에는 재통보를 받게 됩니다. = */
/* = -------------------------------------------------------------------------- = */
/* = -------------------------------------------------------------------------- = */
/* = 03-1. 가상계좌 입금 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
if ( $tx_cd == "TX00" )
{
$sql = " select pp_id, od_id from {$g5['g5_shop_personalpay_table']} where pp_id = '$order_no' and pp_tno = '$tno' ";
$row = sql_fetch($sql);
$result = false;
if($row['pp_id']) {
// 개인결제 UPDATE
$sql = " update {$g5['g5_shop_personalpay_table']}
set pp_receipt_price = '$ipgm_mnyx',
pp_receipt_time = '$tx_tm'
where pp_id = '$order_no'
and pp_tno = '$tno' ";
sql_query($sql, false);
if($row['od_id']) {
// 주문서 UPDATE
$receipt_time = preg_replace("/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})/", "\\1-\\2-\\3 \\4:\\5:\\6", $tx_tm);
$sql = " update {$g5['g5_shop_order_table']}
set od_receipt_price = od_receipt_price + '$ipgm_mnyx',
od_receipt_time = '$tx_tm',
od_shop_memo = concat(od_shop_memo, \"\\n개인결제 ".$row['pp_id']." 로 결제완료 - ".$receipt_time."\")
where od_id = '{$row['od_id']}' ";
$result = sql_query($sql, FALSE);
}
} else {
// 주문서 UPDATE
$sql = " update {$g5['g5_shop_order_table']}
set od_receipt_price = '$ipgm_mnyx',
od_receipt_time = '$tx_tm'
where od_id = '$order_no'
and od_tno = '$tno' ";
$result = sql_query($sql, FALSE);
}
}
if($result) {
if($row['od_id'])
$od_id = $row['od_id'];
else
$od_id = $order_no;
// 주문정보 체크
$sql = " select count(od_id) as cnt
from {$g5['g5_shop_order_table']}
where od_id = '$od_id'
and od_status = '주문' ";
$row = sql_fetch($sql);
if($row['cnt'] == 1) {
// 미수금 정보 업데이트
$info = get_order_info($od_id);
$sql = " update {$g5['g5_shop_order_table']}
set od_misu = '{$info['od_misu']}' ";
if($info['od_misu'] == 0)
$sql .= " , od_status = '입금' ";
$sql .= " where od_id = '$od_id' ";
sql_query($sql, FALSE);
// 장바구니 상태변경
if($info['od_misu'] == 0) {
$sql = " update {$g5['g5_shop_cart_table']}
set ct_status = '입금'
where od_id = '$od_id' ";
sql_query($sql, FALSE);
}
}
}
/* = -------------------------------------------------------------------------- = */
/* = 03-2. 가상계좌 환불 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX01" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-3. 구매확인/구매취소 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX02" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-4. 배송시작 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX03" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-5. 정산보류 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX04" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-6. 즉시취소 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX05" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-7. 취소 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX06" )
{
}
/* = -------------------------------------------------------------------------- = */
/* = 03-7. 발급계좌해지 통보 데이터 DB 처리 작업 부분 = */
/* = -------------------------------------------------------------------------- = */
else if ( $tx_cd == "TX07" )
{
}
/* ============================================================================== */
/* ============================================================================== */
/* = 04. result 값 세팅 하기 = */
/* ============================================================================== */
?>
<html><body><form><input type="hidden" name="result" value="0000"></form></body></html>
<html><body><form><input type="hidden" name="result" value="<?php echo $result; ?>"></form></body></html>