[security] 엑셀/CSV 다운로드 출력값 안전 처리

lib/common.lib.php 에 csv_safe_cell() 헬퍼 추가 (function_exists
가드 포함). 셀 값이 = + - @ TAB CR 로 시작하면 작은따옴표를 prefix
하여 스프레드시트가 수식으로 해석하지 못하도록 변환.

다음 출력 지점에서 사용자 입력 컬럼에 적용:
- adm/shop_admin/orderprintresult.php (CSV + XLS 두 분기)
- adm/shop_admin/orderdeliveryexcel.php
- adm/member_list_exel_export.php
- adm/sms_admin/num_book_file_download.php

호출부는 function_exists('csv_safe_cell') ? ... : 원본 폴백 형태로
감싸서 lib 미업데이트 환경에서도 fatal error 없이 동작하도록 함.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thisgun
2026-05-26 07:30:21 +00:00
co-authored by Claude Opus 4.7
parent 4c00c60006
commit a6fbbe6ad2
5 changed files with 50 additions and 27 deletions
+5 -1
View File
@@ -233,7 +233,11 @@ function member_export_get_data($params)
while ($row = sql_fetch_array($result)) {
$rowData = [];
foreach ($fields as $field) {
$rowData[] = isset($row[$field]) ? $row[$field] : '';
if (isset($row[$field])) {
$rowData[] = function_exists('csv_safe_cell') ? csv_safe_cell($row[$field]) : $row[$field];
} else {
$rowData[] = '';
}
}
$excelData[] = $rowData;
}
+11 -11
View File
@@ -30,17 +30,17 @@ if(! function_exists('column_char')) {
$rows = array();
for($i=1; $row=sql_fetch_array($result); $i++) {
$rows[] =
array(' '.$row['od_id'],
$row['od_name'],
' '.$row['od_tel'],
' '.$row['od_hp'],
$row['od_b_name'],
' '.$row['od_b_tel'],
' '.$row['od_b_hp'],
print_address($row['od_b_addr1'], $row['od_b_addr2'], $row['od_b_addr3'], $row['od_b_addr_jibeon']),
$row['od_delivery_company'],
$row['od_invoice']);
$rows[] =
array(' '.$row['od_id'],
function_exists('csv_safe_cell') ? csv_safe_cell($row['od_name']) : $row['od_name'],
' '.$row['od_tel'],
' '.$row['od_hp'],
function_exists('csv_safe_cell') ? csv_safe_cell($row['od_b_name']) : $row['od_b_name'],
' '.$row['od_b_tel'],
' '.$row['od_b_hp'],
function_exists('csv_safe_cell') ? csv_safe_cell(print_address($row['od_b_addr1'], $row['od_b_addr2'], $row['od_b_addr3'], $row['od_b_addr_jibeon'])) : print_address($row['od_b_addr1'], $row['od_b_addr2'], $row['od_b_addr3'], $row['od_b_addr_jibeon']),
function_exists('csv_safe_cell') ? csv_safe_cell($row['od_delivery_company']) : $row['od_delivery_company'],
function_exists('csv_safe_cell') ? csv_safe_cell($row['od_invoice']) : $row['od_invoice']);
}
$data = array_merge(array($headers), $rows);
+14 -14
View File
@@ -118,21 +118,21 @@ if ($csv == 'csv')
}
echo '"\''.$row['od_b_zip1'].$row['od_b_zip2'].'"\''.',';
echo '"'.$pull_address.'"'.',';
echo '"'.$row['od_b_name'].'"'.',';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell($pull_address) : $pull_address).'"'.',';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell($row['od_b_name']) : $row['od_b_name']).'"'.',';
//echo '"'.multibyte_digit((string)$row[od_b_tel]).'"'.',';
//echo '"'.multibyte_digit((string)$row[od_b_hp]).'"'.',';
echo '"'.conv_telno($row['od_b_tel']) . '"'.',';
echo '"'.conv_telno($row['od_b_hp']) . '"'.',';
echo '"'.preg_replace("/\"/", "&#034;", $row['it_name']) . '"'.',';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell(preg_replace("/\"/", "&#034;", $row['it_name'])) : preg_replace("/\"/", "&#034;", $row['it_name'])) . '"'.',';
echo '"'.$row['ct_qty'].'"'.',';
echo '"'.$row['ct_option'].'"'.',';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell($row['ct_option']) : $row['ct_option']).'"'.',';
echo '"'.$ct_send_cost.'"'.',';
echo '"\''.$row['it_id'].'\'"'.',';
echo '"\''.$row['od_id'].'\'"'.',';
echo '"'.$row['od_invoice'].'"'.',';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell($row['od_invoice']) : $row['od_invoice']).'"'.',';
//echo '"'.preg_replace("/\"/", "&#034;", preg_replace("/\n/", "", $row[od_memo])).'"';
echo '"'.preg_replace("/\"/", "&#034;", $row['od_memo']).'"';
echo '"'.(function_exists('csv_safe_cell') ? csv_safe_cell(preg_replace("/\"/", "&#034;", $row['od_memo'])) : preg_replace("/\"/", "&#034;", $row['od_memo'])).'"';
echo "\n";
}
if ($i == 0)
@@ -217,18 +217,18 @@ if ($csv == 'xls')
}
$rows[] = array(' '.$row['od_b_zip1'].$row['od_b_zip2'],
$pull_address,
$row['od_b_name'],
' '.conv_telno($row['od_b_tel']),
' '.conv_telno($row['od_b_hp']),
preg_replace("/\"/", "&#034;", $row['it_name']),
' '.$row['ct_qty'],
$row['ct_option'],
function_exists('csv_safe_cell') ? csv_safe_cell($pull_address) : $pull_address,
function_exists('csv_safe_cell') ? csv_safe_cell($row['od_b_name']) : $row['od_b_name'],
' '.conv_telno($row['od_b_tel']),
' '.conv_telno($row['od_b_hp']),
function_exists('csv_safe_cell') ? csv_safe_cell(preg_replace("/\"/", "&#034;", $row['it_name'])) : preg_replace("/\"/", "&#034;", $row['it_name']),
' '.$row['ct_qty'],
function_exists('csv_safe_cell') ? csv_safe_cell($row['ct_option']) : $row['ct_option'],
$ct_send_cost,
' '.$row['it_id'],
' '.$row['od_id'],
' '.$row['od_invoice'],
preg_replace("/\"/", "&#034;", $row['od_memo']));
function_exists('csv_safe_cell') ? csv_safe_cell(preg_replace("/\"/", "&#034;", $row['od_memo'])) : preg_replace("/\"/", "&#034;", $row['od_memo']));
}
$data = array_merge(array($headers), $rows);
+1 -1
View File
@@ -46,7 +46,7 @@ for($i=1; $res=sql_fetch_array($qry); $i++)
if ($no_hp && $res['bk_hp'] != '' && !$hp) continue;
$rows[] = array($res['bk_name'], ' '.$hp);
$rows[] = array(function_exists('csv_safe_cell') ? csv_safe_cell($res['bk_name']) : $res['bk_name'], ' '.$hp);
}
$data = array_merge(array($headers), $rows);
+19
View File
@@ -213,6 +213,25 @@ if (!function_exists('get_js_safe_string')) {
}
// CSV/Excel 셀에 출력할 값을 안전하게 변환 (수식 인젝션 방지)
// 값이 = + - @ TAB CR 로 시작하면 작은따옴표를 prefix 해 수식 해석을 차단
if (!function_exists('csv_safe_cell')) {
function csv_safe_cell($v)
{
if ($v === null || $v === '') {
return $v;
}
$s = (string)$v;
$first = substr($s, 0, 1);
if ($first === '=' || $first === '+' || $first === '-' || $first === '@'
|| $first === "\t" || $first === "\r") {
return "'" . $s;
}
return $s;
}
}
// 경고메세지를 경고창으로
function alert($msg='', $url='', $error=true, $post=false)
{