[security]KVE-2026-0687 쇼핑몰 쿠폰 다운로드 TOCTOU Race Condition 수정

MySQL GET_LOCK 으로 동일 회원·동일 쿠폰 다운로드 요청을 직렬화하여,
동시 요청 시 is_coupon_downloaded() 체크를 중복 통과해 쿠폰이 중복
발급되는 문제를 방지함.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thisgun
2026-05-26 03:30:19 +00:00
co-authored by Claude Opus 4.7
parent b12bf551ec
commit efaefbbd0e
+18 -4
View File
@@ -18,13 +18,23 @@ if(!$cp['cz_id'])
if(!($cp['cz_start'] <= G5_TIME_YMD && $cp['cz_end'] >= G5_TIME_YMD))
die(json_encode(array('error' => '다운로드할 수 없는 쿠폰입니다.')));
// 발급여부
if(is_coupon_downloaded($member['mb_id'], $cp['cz_id']))
// 동시 요청으로 인한 쿠폰 중복 발급 방지
$lock_key = 'g5_coupon_dl_'.$cz_id.'_'.addslashes($member['mb_id']);
$lock_row = sql_fetch(" select get_lock('$lock_key', 5) as lk ");
if(empty($lock_row['lk']))
die(json_encode(array('error' => '잠시 후 다시 시도해 주십시오.')));
// 발급여부 (락 획득 후 재확인)
if(is_coupon_downloaded($member['mb_id'], $cp['cz_id'])) {
sql_query(" do release_lock('$lock_key') ");
die(json_encode(array('error' => '이미 다운로드하신 쿠폰입니다.')));
}
// 포인트 쿠폰은 회원포인트 체크
if($cp['cz_type'] && ($member['mb_point'] - $cp['cz_point']) < 0)
if($cp['cz_type'] && ($member['mb_point'] - $cp['cz_point']) < 0) {
sql_query(" do release_lock('$lock_key') ");
die(json_encode(array('error' => '보유하신 포인트가 부족하여 쿠폰을 다운로드할 수 없습니다.')));
}
// 쿠폰발급
$j = 0;
@@ -37,8 +47,10 @@ do {
if(!$row3['cnt'])
break;
else {
if($j > 20)
if($j > 20) {
sql_query(" do release_lock('$lock_key') ");
die(json_encode(array('error' => 'Coupon ID Error')));
}
}
$j++;
} while(1);
@@ -65,4 +77,6 @@ if($result && $cp['cz_type'])
// 다운로드 증가
sql_query(" update {$g5['g5_shop_coupon_zone_table']} set cz_download = cz_download + 1 where cz_id = '$cz_id' ");
sql_query(" do release_lock('$lock_key') ");
die(json_encode(array('error' => '')));