본문으로 건너뛰기

소스 변경 전체 보기

PHP 8 · GODO26
변경 파일 수: 20개
변경 파일 목록
20
변경 파일 코드
추가삭제
파일 업로드 보안 강화
Component/Validator/Validator.php+72-18
변경 전Validator.php
}
 
/**
* 웹쉘이 추가된 이미지 업로드시 eval 포함여부 검증
*
* @param string $tmpName
* @return bool
*/
public static function validateIncludeEval($tmpName) {
 
try {
$fp = fopen($tmpName, 'rb');
$text = fread($fp, filesize($tmpName));
fclose($fp);
 
 
/*
* 1차 : preg_math_all 을 통해 php 구문이 존재할 경우 구문안의 문자열 추출
* 2차 : 구문안의 문자열 중 <? 존재할 경우 explode 배열 만든 후 마지막 <? 데이터만 추출 (예 : <? 문자1 <? 문자2 <? 문자3 ?>)
* 3차 : 최종 추출한 문자열에 불가 문자 여부를 검출 (대소문자 구분 x)
*/
$checkContent = nl2br(str_replace(["\r\n", "\r", "\n"], "", $text));
if (preg_match_all('/(?<=\<\?)(.*?)(?=\?>)/', $checkContent, $matches)) {
for ($i = 0; $i < gd_count($matches[0]); $i++) {
$checkText = $matches[1][$i];
$checkTextArr = explode('<?', $checkText);
$checkVal = $checkTextArr[gd_array_last_key($checkTextArr)];
 
// 추출 문자내 불가 문자 검증
if (preg_match('/eval|\<\?php|\$_POST|\$_GET|\$_REQUEST/i', $checkVal)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
return false;
}
}
 
변경 후Validator.php
}
 
/**
* 웹쉘이 추가된 이미지 업로드시 PHP 코드 포함여부 검증
*
* @param string $tmpName
* @return bool true=안전, false=차단
*/
public static function validateIncludeEval($tmpName): bool
{
try {
$text = file_get_contents($tmpName);
if ($text === false) {
return false;
}
 
/*
* PHP 열림 태그(<?php / <?=)부터 닫힘 태그(또는 파일 끝)까지 추출 — 닫힘 태그 우회 차단 + 바이너리 false positive 회피.
* 열림 태그를 <?php / <?= 로 한정: 바이너리에 우연히 나온 <? 가 거대한 가짜 PHP 블록을 만들어 정상 이미지를 오탐하던 문제 차단.
* /s 모디파이어로 . 가 newline 도 매칭하여 nl2br 트릭 불필요. /i 로 <?PHP 대소문자 무시.
*/
if (preg_match_all('/<\?(?:php|=)(.*?)(?:\?>|$)/si', $text, $matches)) {
$dangerPatterns = [
'variable_access' => [
'description' => '슈퍼글로벌 변수 및 변수 변수 접근',
'patterns' => [
'\$_(?:GET|POST|REQUEST|COOKIE|SERVER|FILES|ENV|SESSION)',
'\$\{\s*[\'"]?_(?:GET|POST|REQUEST|COOKIE|SERVER|FILES|ENV|SESSION)',
'\$\$\w+',
]
],
'code_execution' => [
'description' => '코드 실행 함수',
'patterns' => [
'\beval\s*\(',
'\bassert\s*\(',
'\bcreate_function\s*\(',
]
],
'system_commands' => [
'description' => '시스템 명령 실행',
'patterns' => [
'\b(?:system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec)\s*\(',
]
],
'file_operations' => [
'description' => '파일 포함 및 읽기/쓰기',
'patterns' => [
'\b(?:include|require)(?:_once)?\b',
'\b(?:file_get_contents|file_put_contents|fopen|readfile|file)\s*\(',
]
],
'dynamic_calls' => [
'description' => '동적 함수 호출',
'patterns' => [
'\bcall_user_func(?:_array)?\s*\(',
]
],
'encoding_functions' => [
'description' => '인코딩/디코딩 함수 (코드 실행 가능)',
'patterns' => [
'\b(?:base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin|convert_uudecode)\s*\(',
]
],
'regex_functions' => [
'description' => '정규식 함수 (코드 실행 가능)',
'patterns' => [
'\bpreg_replace\s*\(',
]
],
'stream_wrappers' => [
'description' => 'PHP 스트림 래퍼',
'patterns' => [
'php:\/\/',
]
],
];
 
$combinedPattern = '/' . implode('|', array_merge(...array_column($dangerPatterns, 'patterns'))) . '/i';
 
foreach ($matches[1] as $phpContent) {
if (preg_match($combinedPattern, $phpContent)) {
return false;
}
}
 
게시판 및 상품 안내 보안 강화
Component/Agreement/BuyerInform.php+6-3
변경 전BuyerInform.php
$tableName = $this->mall->getTableName(DB_BUYER_INFORM, $mallSno);
 
$arrField = "*,s.companyNm as scmNm";
 
 
if ($mallSno > DEFAULT_MALL_NUMBER) {
$strSQL = "SELECT " . $arrField . " FROM " . $tableName . " INNER JOIN " . DB_SCM_MANAGE . " as s ON s.scmNo = " . $tableName . ".scmNo WHERE informCd='" . $informCd . "' AND mallSno='" . $mallSno . "' LIMIT 1";
 
} else {
$strSQL = "SELECT " . $arrField . " FROM " . $tableName . " INNER JOIN " . DB_SCM_MANAGE . " as s ON s.scmNo = " . $tableName . ".scmNo WHERE informCd='" . $informCd . "' LIMIT 1";
}
$data = $this->db->fetch($strSQL);
 
if (Session::get('manager.isProvider')) {
if($data['scmDisplayFl'] =='n' && $data['scmNo'] != Session::get('manager.scmNo')) {
 
변경 후BuyerInform.php
$tableName = $this->mall->getTableName(DB_BUYER_INFORM, $mallSno);
 
$arrField = "*,s.companyNm as scmNm";
$arrBind = [];
$this->db->bind_param_push($arrBind, 's', $informCd);
if ($mallSno > DEFAULT_MALL_NUMBER) {
$this->db->bind_param_push($arrBind, 'i', $mallSno);
$strSQL = "SELECT " . $arrField . " FROM " . $tableName . " INNER JOIN " . DB_SCM_MANAGE . " as s ON s.scmNo = " . $tableName . ".scmNo WHERE informCd = ? AND mallSno = ? LIMIT 1";
} else {
$strSQL = "SELECT " . $arrField . " FROM " . $tableName . " INNER JOIN " . DB_SCM_MANAGE . " as s ON s.scmNo = " . $tableName . ".scmNo WHERE informCd = ? LIMIT 1";
}
$data = $this->db->query_fetch($strSQL, $arrBind, false);
 
if (Session::get('manager.isProvider')) {
if($data['scmDisplayFl'] =='n' && $data['scmNo'] != Session::get('manager.scmNo')) {
 
Component/Board/ArticleAdmin.php+4-2
변경 전ArticleAdmin.php
return '네이버페이 구매자';
}
if ($data[$refManagerSno] < 0) { //비회원일때
$query = "SELECT scmNo,managerNickNm,dispImage,managerId,managerNm,isDelete FROM " . DB_MANAGER . " WHERE sno = " . abs($data[$refManagerSno]);
$managerData = $this->db->query_fetch($query, null, false);
 
 
$addInfo = '';
if ($managerData['isDelete'] == 'y') {
$addInfo = '<br><span class="text-red">(' . Manager::DELETE_DISPLAY . ')</span>'; //TODO:운영자표기
 
변경 후ArticleAdmin.php
return '네이버페이 구매자';
}
if ($data[$refManagerSno] < 0) { //비회원일때
$arrBind = [];
$this->db->bind_param_push($arrBind, 'i', abs($data[$refManagerSno]));
$query = "SELECT scmNo,managerNickNm,dispImage,managerId,managerNm,isDelete FROM " . DB_MANAGER . " WHERE sno = ?";
$managerData = $this->db->query_fetch($query, $arrBind, false);
$addInfo = '';
if ($managerData['isDelete'] == 'y') {
$addInfo = '<br><span class="text-red">(' . Manager::DELETE_DISPLAY . ')</span>'; //TODO:운영자표기
 
Component/Board/Board.php+2-2
변경 전Board.php
if($this->req['mode'] == 'write') {
$groupNo = Session::get('groupNo_' . $this->cfg['bdId']);
if (gd_isset($groupNo)) {
$sql = 'SELECT regDt FROM ' . DB_BD_ . $this->req['bdId'] . ' WHERE groupNo = ? AND groupThread = \'\'';
$res = $this->db->query_fetch($sql, ['i', $groupNo]);
if (empty($res[0]['regDt']) === false && $res[0]['regDt'] < date('Y-m-d H:i:s', strtotime('-30 seconds'))) {
$groupNo = BoardUtil::createGroupNo($this->cfg['bdId']);
$groupNo = BoardUtil::createGroupNo($this->cfg['bdId']);
Session::set('groupNo_' . $this->cfg['bdId'], $groupNo);
}
$query = "SELECT count(*) as cnt FROM " . DB_BD_ . $this->req['bdId'] . " WHERE groupNo <= ? AND groupThread = '' AND regDt >= (now()-INTERVAL 30 SECOND) AND writerIp = ? AND subject = ? AND memNo = ? ";
$arrBind = [];
$this->db->bind_param_push($arrBind, 's', $groupNo);
$this->db->bind_param_push($arrBind, 's', \Request::getRemoteAddress());
 
변경 후Board.php
if($this->req['mode'] == 'write') {
$groupNo = Session::get('groupNo_' . $this->cfg['bdId']);
if (gd_isset($groupNo)) {
$sql = 'SELECT regDt FROM ' . DB_BD_ . $this->cfg['bdId'] . ' WHERE groupNo = ? AND groupThread = \'\'';
$res = $this->db->query_fetch($sql, ['i', $groupNo]);
if (empty($res[0]['regDt']) === false && $res[0]['regDt'] < date('Y-m-d H:i:s', strtotime('-30 seconds'))) {
$groupNo = BoardUtil::createGroupNo($this->cfg['bdId']);
$groupNo = BoardUtil::createGroupNo($this->cfg['bdId']);
Session::set('groupNo_' . $this->cfg['bdId'], $groupNo);
}
$query = "SELECT count(*) as cnt FROM " . DB_BD_ . $this->cfg['bdId'] . " WHERE groupNo <= ? AND groupThread = '' AND regDt >= (now()-INTERVAL 30 SECOND) AND writerIp = ? AND subject = ? AND memNo = ? ";
$arrBind = [];
$this->db->bind_param_push($arrBind, 's', $groupNo);
$this->db->bind_param_push($arrBind, 's', \Request::getRemoteAddress());
 
플러스리뷰 위젯 보안 강화
Admin/board/plus_review_widget_preview.php+3-3
변경 전plus_review_widget_preview.php
 
 
<script>
var thumSizeType = '<?=$req['thumSizeType']?>';
$(document).ready(function () {
<?php if ($req['mode'] != 'article') { ?>
setTimeout(function () {
if (thumSizeType == 'menual') {
$('.plus-review-widget-preview-photo img').css('width', '<?=$req['thumWidth']?>px').css('height', '<?=$req['thumWidth']?>px').show();
} else {
var containerWidth = ($('.plus-review-widget').width());
var autoSize = Math.floor(containerWidth /<?=$req['cols']?>) - 10;
$('.plus-review-widget-preview-photo').css('width', autoSize + 'px').css('height', autoSize + 'px');
$('.plus-review-widget-preview-photo img').css('width', '100%').css('height', '100%');
$('.plus-review-widget-preview-photo img').show();
 
변경 후plus_review_widget_preview.php
 
 
<script>
var thumSizeType = <?= json_encode($req['thumSizeType'] ?? '') ?>;
$(document).ready(function () {
<?php if ($req['mode'] != 'article') { ?>
setTimeout(function () {
if (thumSizeType == 'menual') {
$('.plus-review-widget-preview-photo img').css('width', '<?= intval($req['thumWidth'] ?? 0) ?>px').css('height', '<?= intval($req['thumWidth'] ?? 0) ?>px').show();
} else {
var containerWidth = ($('.plus-review-widget').width());
var autoSize = Math.floor(containerWidth /<?= max(1, intval($req['cols'] ?? 1)) ?>) - 10;
$('.plus-review-widget-preview-photo').css('width', autoSize + 'px').css('height', autoSize + 'px');
$('.plus-review-widget-preview-photo img').css('width', '100%').css('height', '100%');
$('.plus-review-widget-preview-photo img').show();
 
구매 리뷰 보안 강화
Component/Board/BoardFront.php+34-1
변경 전BoardFront.php
 
if ($this->cfg['bdReviewAuthWrite'] == 'all') { //구매여부와 상관없이 후기작성
if(empty($goodsNo) && $orderGoodsNo) {
 
 
 
$order = new Order();
$orderGoodsData = $order->getOrderGoods(null, $orderGoodsNo)[0];
$goodsNo = $orderGoodsData['goodsNo'];
if ($orderGoodsNo) {
$order = new Order();
$orderGoodsData = $order->getOrderGoods(null, $orderGoodsNo)[0];
if (empty($orderGoodsData)) {
return ['possible' => false, 'errorMsg' => __('해당 상품의 주문내역이 없습니다.')];
}
if ($this->isExceptReviewGoods($orderGoodsData['goodsNo']) === true || $orderGoodsData['goodsType'] == 'addGoods') {
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 상품후기 작성 시 주문조건관련 애러메시지 가져오는 함수. false 이면 애러없음.
*
 
변경 후BoardFront.php
 
if ($this->cfg['bdReviewAuthWrite'] == 'all') { //구매여부와 상관없이 후기작성
if(empty($goodsNo) && $orderGoodsNo) {
if ($this->isOwnedOrderGoods($orderGoodsNo, $isGuest) === false) {
return ['possible' => false, 'errorMsg' => __('해당 상품의 주문내역이 없습니다.')];
}
$order = new Order();
$orderGoodsData = $order->getOrderGoods(null, $orderGoodsNo)[0];
$goodsNo = $orderGoodsData['goodsNo'];
if ($orderGoodsNo) {
$order = new Order();
$orderGoodsData = $order->getOrderGoods(null, $orderGoodsNo)[0];
if (empty($orderGoodsData) || $this->isOwnedOrderGoods($orderGoodsNo, $isGuest) === false) {
return ['possible' => false, 'errorMsg' => __('해당 상품의 주문내역이 없습니다.')];
}
if ($this->isExceptReviewGoods($orderGoodsData['goodsNo']) === true || $orderGoodsData['goodsType'] == 'addGoods') {
}
}
 
/**
* 주문 상품(orderGoodsNo)이 현재 요청자 소유인지 검증
*
* 회원 : $this->member['memNo']
* 게스트 : 세션(guest.orderNo)
* 비로그인: $this->member['memNo'] == null
*
* @param int $orderGoodsNo 주문상품번호
* @param bool $isGuest 비회원 주문조회 로그인 여부
* @return bool 본인 주문상품이면 true
*/
protected function isOwnedOrderGoods($orderGoodsNo, $isGuest)
{
$arrBind = [];
$query = "SELECT count(*) as cnt FROM " . DB_ORDER_GOODS . " as og INNER JOIN " . DB_ORDER . " as o ON og.orderNo = o.orderNo WHERE og.sno = ? AND ";
$this->db->bind_param_push($arrBind, 'i', $orderGoodsNo);
if ($isGuest) {
$query .= "og.orderNo = ? AND o.memNo = 0 ";
$this->db->bind_param_push($arrBind, 's', \Session::get('guest.orderNo'));
} else {
// 비로그인 시 $this->member 미설정 → memNo null (PHP8 array-offset-on-null 경고 방지) → 매칭 0건
$memNo = is_array($this->member) ? ($this->member['memNo'] ?? null) : null;
$query .= "o.memNo = ? ";
$this->db->bind_param_push($arrBind, 'i', $memNo);
}
$result = $this->db->query_fetch($query, $arrBind, false);
 
return ($result['cnt'] > 0);
}
 
/**
* 상품후기 작성 시 주문조건관련 애러메시지 가져오는 함수. false 이면 애러없음.
*
 
정렬 조건 보안 강화.
Component/Agreement/BuyerInform.php+18-11
변경 전BuyerInform.php
 
const TEXT_USELESS_VALUE = '%s은(는) 사용할 수 없습니다.';
 
 
 
// informCd,informNm,groupCd
private $goodsInfo = [
'002' => 'goodsInfoDelivery',
$this->search['keyword'] = gd_isset($getValue['keyword']);
$this->search['groupCd'] = gd_isset($getValue['groupCd']);
$this->search['modeFl'] = gd_isset($getValue['modeFl']);
$this->search['sort'] = gd_isset($getValue['sort'], 'modeFl asc,regDt desc');
//검색설정
$this->search['sortList'] = [
'regDt desc' => __('등록일 ↑'),
$this->arrBind = null;
}
 
// --- 정렬 설정
$sort = gd_isset($getValue['sort']);
if (empty($sort)) {
$sort = 'b.modeFl desc';
}
 
 
if ($mode == 'layer') {
// --- 페이지 기본설정
if (gd_isset($getValue['pagelink'])) {
$this->search['keyword'] = gd_isset($getValue['keyword']);
$this->search['groupCd'] = gd_isset($getValue['groupCd']);
$this->search['modeFl'] = gd_isset($getValue['modeFl']);
$this->search['sort'] = gd_isset($getValue['sort'], 'modeFl asc,regDt desc');
 
 
 
 
 
 
$this->search['scmFl'] = gd_isset($getValue['scmFl'], '');
$this->search['scmNo'] = gd_isset($getValue['scmNo'], (string) Session::get('manager.scmNo'));
 
$this->arrBind = null;
}
 
// --- 정렬 설정
$sort = gd_isset($getValue['sort']);
if (empty($sort)) {
$sort = 'b.modeFl desc';
}
 
// --- 페이지 기본설정
 
변경 후BuyerInform.php
 
const TEXT_USELESS_VALUE = '%s은(는) 사용할 수 없습니다.';
 
const DEFAULT_SORT_INFO = 'b.modeFl asc, b.regDt desc';
 
// informCd,informNm,groupCd
private $goodsInfo = [
'002' => 'goodsInfoDelivery',
$this->search['keyword'] = gd_isset($getValue['keyword']);
$this->search['groupCd'] = gd_isset($getValue['groupCd']);
$this->search['modeFl'] = gd_isset($getValue['modeFl']);
$this->search['sort'] = gd_isset($getValue['sort'], self::DEFAULT_SORT_INFO);
//검색설정
$this->search['sortList'] = [
'regDt desc' => __('등록일 ↑'),
$this->arrBind = null;
}
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$sort = gd_isset($getValue['sort'], '');
if (!array_key_exists($sort, $this->search['sortList'])) {
$sort = self::DEFAULT_SORT_INFO;
}
 
 
if ($mode == 'layer') {
// --- 페이지 기본설정
if (gd_isset($getValue['pagelink'])) {
$this->search['keyword'] = gd_isset($getValue['keyword']);
$this->search['groupCd'] = gd_isset($getValue['groupCd']);
$this->search['modeFl'] = gd_isset($getValue['modeFl']);
$this->search['sort'] = gd_isset($getValue['sort'], self::DEFAULT_SORT_INFO);
$this->search['sortList'] = [
'regDt desc' => __('등록일 ↑'),
'regDt asc' => __('등록일 ↓'),
'informNm desc' => __('이용안내 제목 ↑'),
'informNm asc' => __('이용안내 제목 ↓'),
];
$this->search['scmFl'] = gd_isset($getValue['scmFl'], '');
$this->search['scmNo'] = gd_isset($getValue['scmNo'], (string) Session::get('manager.scmNo'));
 
$this->arrBind = null;
}
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$sort = gd_isset($getValue['sort'], '');
if (!array_key_exists($sort, $this->search['sortList'])) {
$sort = self::DEFAULT_SORT_INFO;
}
 
// --- 페이지 기본설정
 
Component/Board/BoardTemplate.php+13-9
변경 전BoardTemplate.php
const ECT_INVALID_ARG = '%s.ECT_INVALID_ARG';
const ECT_WRONG_MODE = '%s.ECT_WRONG_MODE';
const TEXT_WRONG_MODE = '잘못된 설정값입니다.';
 
 
protected $db;
protected $fieldTypes = null;
//--- 페이지 설정
gd_isset($req['page'], 1);
gd_isset($req['pageNum'], 10);
gd_isset($req['sort'], 'sno desc');
 
 
 
 
 
 
 
 
 
 
 
 
 
$countQuery = "SELECT COUNT(sno) as cnt FROM " . DB_BOARD_TEMPLATE . " WHERE 1 ";
if ($arrWhere) {
$this->db->strWhere = gd_implode(' and ', $arrWhere);
}
$this->db->strLimit = '?, ?';
$this->db->strOrder = $req['sort'];
 
$this->db->bind_param_push($arrBind, 'i', (($req['page'] - 1) * $req['pageNum']));
$this->db->bind_param_push($arrBind, 'i', $req['pageNum']);
}
 
 
$getData['sort'] = [
'sno desc' => '번호↓',
'sno asc' => '번호↑',
'regDt desc' => '등록일↓',
'regDt asc' => '등록일↑',
];
 
//--- 각 데이터 배열화
$getData['data'] = $data;
$getData['amountCnt'] = $amountCnt;
 
변경 후BoardTemplate.php
const ECT_INVALID_ARG = '%s.ECT_INVALID_ARG';
const ECT_WRONG_MODE = '%s.ECT_WRONG_MODE';
const TEXT_WRONG_MODE = '잘못된 설정값입니다.';
const DEFAULT_SORT_BOARD_TEMPLATE = 'sno desc';
 
protected $db;
protected $fieldTypes = null;
//--- 페이지 설정
gd_isset($req['page'], 1);
gd_isset($req['pageNum'], 10);
 
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$getData['sort'] = [
'sno desc' => '번호↓',
'sno asc' => '번호↑',
'regDt desc' => '등록일↓',
'regDt asc' => '등록일↑',
];
$sort = gd_isset($req['sort'], self::DEFAULT_SORT_BOARD_TEMPLATE);
if (!array_key_exists($sort, $getData['sort'])) {
$sort = self::DEFAULT_SORT_BOARD_TEMPLATE;
}
 
$countQuery = "SELECT COUNT(sno) as cnt FROM " . DB_BOARD_TEMPLATE . " WHERE 1 ";
if ($arrWhere) {
$this->db->strWhere = gd_implode(' and ', $arrWhere);
}
$this->db->strLimit = '?, ?';
$this->db->strOrder = $sort;
 
$this->db->bind_param_push($arrBind, 'i', (($req['page'] - 1) * $req['pageNum']));
$this->db->bind_param_push($arrBind, 'i', $req['pageNum']);
}
 
 
 
 
 
 
 
 
 
//--- 각 데이터 배열화
$getData['data'] = $data;
$getData['amountCnt'] = $amountCnt;
 
Component/Category/CategoryAdmin.php+17-8
변경 전CategoryAdmin.php
 
const TEXT_NOT_EXIST_VALUE = '%s 필수 항목이 존재하지 않습니다.';
 
 
 
 
 
 
 
 
protected $arrBind = array();
// 리스트 검색관련
protected $arrWhere = array();
$this->arrBind = null;
}
 
// --- 정렬 설정
$sort['fieldName'] = gd_isset($_GET['sort']['name']);
$sort['sortMode'] = gd_isset($_GET['sort']['mode']);
if (empty($sort['fieldName']) || empty($sort['sortMode'])) {
$sort['fieldName'] = 'regDt';
$sort['sortMode'] = 'desc';
 
}
 
// --- 페이지 기본설정
 
$this->arrWhere[] = 'divisionFl = \'n\'';
 
// --- 정렬 설정
$sort['fieldName'] = gd_isset($getValue['sort']['name']);
$sort['sortMode'] = gd_isset($getValue['sort']['mode']);
if (empty($sort['fieldName']) || empty($sort['sortMode'])) {
$sort['fieldName'] = 'regDt';
$sort['sortMode'] = 'desc';
 
}
 
 
 
변경 후CategoryAdmin.php
 
const TEXT_NOT_EXIST_VALUE = '%s 필수 항목이 존재하지 않습니다.';
 
// 정렬 화이트리스트 (SQL Injection 방지)
const ALLOWED_SORT_FIELDS_CATEGORY = ['regDt', 'cateNm', 'cateCd', 'cateSort', 'sno', 'goodsCnt'];
const ALLOWED_SORT_FIELDS_CATEGORY_THEME = ['regDt', 'themeNm', 'sortFl', 'imageCd', 'sno'];
const ALLOWED_SORT_MODES = ['asc', 'desc'];
const DEFAULT_SORT_FIELD = 'regDt';
const DEFAULT_SORT_MODE = 'desc';
 
protected $arrBind = array();
// 리스트 검색관련
protected $arrWhere = array();
$this->arrBind = null;
}
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$sort['fieldName'] = gd_isset($_GET['sort']['name']);
$sort['sortMode'] = gd_isset($_GET['sort']['mode']);
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_CATEGORY_THEME, true)
|| !in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['fieldName'] = self::DEFAULT_SORT_FIELD;
$sort['sortMode'] = self::DEFAULT_SORT_MODE;
}
 
// --- 페이지 기본설정
 
$this->arrWhere[] = 'divisionFl = \'n\'';
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$sort['fieldName'] = gd_isset($getValue['sort']['name']);
$sort['sortMode'] = gd_isset($getValue['sort']['mode']);
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_CATEGORY, true)
|| !in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['fieldName'] = self::DEFAULT_SORT_FIELD;
$sort['sortMode'] = self::DEFAULT_SORT_MODE;
}
 
 
 
Component/Coupon/CouponAdmin.php+34
변경 전CouponAdmin.php
'couponNm DESC' => '쿠폰명 가나다역순',
];
 
 
 
 
 
 
 
/**
* 쿠폰 리스트 가져오기
*
} else {
$sort['fieldName'] = 'c' . $sort['fieldName'];
}
 
 
 
 
 
 
 
 
// 레이어에서 자바스크립트 페이징 처리시 사용되는 구문
if (gd_isset($getValue['pagelink'])) {
} else {
$sort['fieldName'] = 'mc.' . $sort['fieldName'];
}
 
 
 
 
 
 
 
 
gd_isset($getValue['page'], 1);
gd_isset($getValue['pageNum'], 10);
} else {
$sort['fieldName'] = 'mc.' . $sort['fieldName'];
}
 
 
 
 
 
 
 
 
gd_isset($getValue['page'], 1);
gd_isset($getValue['pageNum'], 10);
} else {
$sort['fieldName'] = 'c' . $sort['fieldName'];
}
 
 
 
 
 
 
 
 
// 레이어에서 자바스크립트 페이징 처리시 사용되는 구문
if (gd_isset($getValue['pagelink'])) {
 
변경 후CouponAdmin.php
'couponNm DESC' => '쿠폰명 가나다역순',
];
 
// 정렬 화이트리스트 (SQL Injection 방지) - UI 없어 default 만 허용
const ALLOWED_SORT_FIELDS_COUPON = ['c.regDt'];
const ALLOWED_SORT_FIELDS_MEMBER_COUPON = ['mc.regDt'];
const ALLOWED_SORT_FIELDS_COMEBACK_COUPON = ['cc.regDt'];
const ALLOWED_SORT_MODES = ['asc', 'desc'];
 
/**
* 쿠폰 리스트 가져오기
*
} else {
$sort['fieldName'] = 'c' . $sort['fieldName'];
}
// 화이트리스트 검증 — SQL Injection 방지
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_COUPON, true)) {
$sort['fieldName'] = 'c.regDt';
}
if (!in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['sortMode'] = 'desc';
}
 
// 레이어에서 자바스크립트 페이징 처리시 사용되는 구문
if (gd_isset($getValue['pagelink'])) {
} else {
$sort['fieldName'] = 'mc.' . $sort['fieldName'];
}
// 화이트리스트 검증 — SQL Injection 방지
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_MEMBER_COUPON, true)) {
$sort['fieldName'] = 'mc.regDt';
}
if (!in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['sortMode'] = 'desc';
}
 
gd_isset($getValue['page'], 1);
gd_isset($getValue['pageNum'], 10);
} else {
$sort['fieldName'] = 'mc.' . $sort['fieldName'];
}
// 화이트리스트 검증 — SQL Injection 방지
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_MEMBER_COUPON, true)) {
$sort['fieldName'] = 'mc.regDt';
}
if (!in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['sortMode'] = 'desc';
}
 
gd_isset($getValue['page'], 1);
gd_isset($getValue['pageNum'], 10);
} else {
$sort['fieldName'] = 'c' . $sort['fieldName'];
}
// 화이트리스트 검증 — SQL Injection 방지
if (!in_array($sort['fieldName'], self::ALLOWED_SORT_FIELDS_COMEBACK_COUPON, true)) {
$sort['fieldName'] = 'cc.regDt';
}
if (!in_array($sort['sortMode'], self::ALLOWED_SORT_MODES, true)) {
$sort['sortMode'] = 'desc';
}
 
// 레이어에서 자바스크립트 페이징 처리시 사용되는 구문
if (gd_isset($getValue['pagelink'])) {
 
Component/Member/Manager.php+8-1
변경 전Manager.php
 
const DEFAULT_SESSION_LIMIT_TIME = SIX_HOURS;
const DEFAULT_CS_LIMIT_TIME = THREE_HOURS;
 
 
 
/**
* @var array SMS 자동발송 수신 종류
*/
'lastLoginDt asc' => __('최종 로그인') . '↑',
];
 
$sortQuery = gd_isset($req['sort'], 'regDt desc');
 
 
 
 
// --- 검색 설정
$this->search['detailSearch'] = gd_isset($req['detailSearch']);
$this->search['key'] = gd_isset($req['key']);
 
변경 후Manager.php
 
const DEFAULT_SESSION_LIMIT_TIME = SIX_HOURS;
const DEFAULT_CS_LIMIT_TIME = THREE_HOURS;
 
// 정렬 화이트리스트 default (SQL Injection 방지) — getManagerList 의 sortList 와 함께 검증
const DEFAULT_SORT_MANAGER = 'regDt desc';
/**
* @var array SMS 자동발송 수신 종류
*/
'lastLoginDt asc' => __('최종 로그인') . '↑',
];
 
// 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$sortQuery = gd_isset($req['sort'], self::DEFAULT_SORT_MANAGER);
if (!array_key_exists($sortQuery, $this->search['sortList'])) {
$sortQuery = self::DEFAULT_SORT_MANAGER;
}
// --- 검색 설정
$this->search['detailSearch'] = gd_isset($req['detailSearch']);
$this->search['key'] = gd_isset($req['key']);
 
Component/Order/OrderAdminNew.php+42-10
변경 전OrderAdminNew.php
const TEXT_PASSWORD_INVALID = '%s은(는) 비밀번호형식에 맞지 않습니다.';
const NOT_REQUEST_ORDER_STATUS = '해당 주문단계에서는 신청을 하실 수 없습니다.';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
protected $orderGridConfigList; //주문그리드 디폴트 설정 항목
protected $naverpayConfig; //네이버페이 설정
protected $paycoConfig; //페이코쇼핑 설정
$page = \App::load('\\Component\\Page\\Page', $searchData['page'],0,0,$searchData['pageNum']);
$page->setCache(true)->setUrl(\Request::getQueryString()); // 페이지당 리스트 수
 
 
 
 
 
 
// 주문상태 정렬 예외 케이스 처리
if ($searchData['sort'] == 'og.orderStatus asc') {
$searchData['sort'] = 'case LEFT(og.orderStatus, 1) when \'o\' then \'01\' when \'p\' then \'02\' when \'g\' then \'03\' when \'d\' then \'04\' when \'s\' then \'05\' when \'e\' then \'06\' when \'b\' then \'07\' when \'r\' then \'08\' when \'c\' then \'09\' when \'f\' then \'10\' else \'11\' end';
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// 정렬 설정
$orderSort = gd_isset($searchData['sort'], 'o.regDt desc');
 
 
 
 
// 사용 필드
$arrIncludeOg = [
$this->search['keyword'] = gd_isset($request['keyword']);
$this->search['useFl'] = gd_isset($request['useFl']);
$this->search['sort'] = gd_isset($request['sort'], 'regDt desc');
 
 
 
 
 
$this->checked['useFl'][$this->search['useFl']] = 'checked="checked"';
 
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// --- 정렬 설정
$orderSort = gd_isset($searchData['sort'], 'oi.regDt desc, oi.scmNo asc, oi.orderGoodsNo asc');
 
 
 
 
 
// 추출 필드
$arrIncludeOi = [
$this->search['sortList'] = [
'ofa.regDt desc' => sprintf('%s↓', __('등록일')),
'ofa.regDt asc' => sprintf('%s↑', __('등록일')),
'ofa.group desc' => sprintf('%s↓', __('그룹')),
'ofa.group asc' => sprintf('%s↑', __('그룹')),
'ofa.name desc' => sprintf('%s↓', __('이름')),
'ofa.name asc' => sprintf('%s↑', __('이름')),
];
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][0], date('Y-m-d', strtotime('-' . $this->search['periodFl'] . ' day')));
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][1], date('Y-m-d'));
$this->search['sort'] = gd_isset($searchData['sort'], gd_array_keys($this->search['sortList'])[0]);
 
 
 
 
$setData['search'] = $this->search;
 
// 체크 설정
$this->db->strField = ' aogm.*, mn.managerId, mn.managerNm, c.itemNm ';
$this->db->strWhere = gd_implode(' AND ', $arrWhere);
$this->db->strJoin = DB_ADMIN_ORDER_GOODS_MEMO . ' AS aogm LEFT JOIN ' . DB_CODE . ' AS c ON c.itemCd = aogm.memoCd LEFT JOIN ' . DB_MANAGER . ' AS mn ON aogm.managerSno=mn.sno';
if($requestParams['sort']) {
$this->db->strOrder = $requestParams['sort'];
} else {
$this->db->strOrder = 'aogm.regDt DESC';
}
 
$offset = $requestParams['page'] < 1 ? 0 : ($requestParams['page'] - 1) * $requestParams['pageNum'];
$this->db->strLimit = $offset . ',' . $requestParams['pageNum'];
 
변경 후OrderAdminNew.php
const TEXT_PASSWORD_INVALID = '%s은(는) 비밀번호형식에 맞지 않습니다.';
const NOT_REQUEST_ORDER_STATUS = '해당 주문단계에서는 신청을 하실 수 없습니다.';
 
// 정렬 화이트리스트 (SQL Injection 방지)
const ALLOWED_SORT_ORDER = [
'o.regDt desc', 'o.regDt asc', // 주문일
'og.orderNo desc', 'og.orderNo asc', // 주문번호
'o.orderGoodsNm desc', 'o.orderGoodsNm asc', // 상품명
'oi.orderName desc', 'oi.orderName asc', // 주문자
'o.settlePrice desc', 'o.settlePrice asc', // 총 결제금액
'oi.receiverName desc', 'oi.receiverName asc', // 수령자
'sm.companyNm desc', 'sm.companyNm asc', // 공급사
'og.orderStatus desc', 'og.orderStatus asc', // 처리상태
'og.paymentDt desc', 'og.paymentDt asc', // 결제일
'oi.packetCode desc', 'oi.packetCode asc', // 묶음배송
'ouh.regDt desc', 'ouh.regDt asc', // 신청일
];
 
protected $orderGridConfigList; //주문그리드 디폴트 설정 항목
protected $naverpayConfig; //네이버페이 설정
protected $paycoConfig; //페이코쇼핑 설정
$page = \App::load('\\Component\\Page\\Page', $searchData['page'],0,0,$searchData['pageNum']);
$page->setCache(true)->setUrl(\Request::getQueryString()); // 페이지당 리스트 수
 
// 정렬 화이트리스트 검증 — SQL Injection 방지 (CASE WHEN 치환 전에 검증)
if (!empty($searchData['sort']) && !in_array($searchData['sort'], self::ALLOWED_SORT_ORDER, true)) {
$searchData['sort'] = '';
}
 
// 주문상태 정렬 예외 케이스 처리
if ($searchData['sort'] == 'og.orderStatus asc') {
$searchData['sort'] = 'case LEFT(og.orderStatus, 1) when \'o\' then \'01\' when \'p\' then \'02\' when \'g\' then \'03\' when \'d\' then \'04\' when \'s\' then \'05\' when \'e\' then \'06\' when \'b\' then \'07\' when \'r\' then \'08\' when \'c\' then \'09\' when \'f\' then \'10\' else \'11\' end';
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$orderSort = gd_isset($searchData['sort'], 'o.regDt desc');
if (!in_array($orderSort, self::ALLOWED_SORT_ORDER, true)) {
$orderSort = 'o.regDt desc';
}
 
// 사용 필드
$arrIncludeOg = [
$this->search['keyword'] = gd_isset($request['keyword']);
$this->search['useFl'] = gd_isset($request['useFl']);
$this->search['sort'] = gd_isset($request['sort'], 'regDt desc');
// 정렬 화이트리스트 검증 — SQL Injection 방지
if (!array_key_exists($this->search['sort'], $this->search['sortList'])) {
$this->search['sort'] = 'regDt desc';
}
 
$this->checked['useFl'][$this->search['useFl']] = 'checked="checked"';
 
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$defaultInvoiceSort = 'oi.regDt desc, oi.scmNo asc, oi.orderGoodsNo asc';
$orderSort = gd_isset($searchData['sort'], $defaultInvoiceSort);
if (!in_array($orderSort, self::ALLOWED_SORT_ORDER, true) && $orderSort !== $defaultInvoiceSort) {
$orderSort = $defaultInvoiceSort;
}
 
// 추출 필드
$arrIncludeOi = [
$this->search['sortList'] = [
'ofa.regDt desc' => sprintf('%s↓', __('등록일')),
'ofa.regDt asc' => sprintf('%s↑', __('등록일')),
'ofa.groupNm desc' => sprintf('%s↓', __('그룹')),
'ofa.groupNm asc' => sprintf('%s↑', __('그룹')),
'ofa.name desc' => sprintf('%s↓', __('이름')),
'ofa.name asc' => sprintf('%s↑', __('이름')),
];
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][0], date('Y-m-d', strtotime('-' . $this->search['periodFl'] . ' day')));
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][1], date('Y-m-d'));
$this->search['sort'] = gd_isset($searchData['sort'], gd_array_keys($this->search['sortList'])[0]);
// 정렬 화이트리스트 검증 — SQL Injection 방지
if (!array_key_exists($this->search['sort'], $this->search['sortList'])) {
$this->search['sort'] = array_keys($this->search['sortList'])[0];
}
$setData['search'] = $this->search;
 
// 체크 설정
$this->db->strField = ' aogm.*, mn.managerId, mn.managerNm, c.itemNm ';
$this->db->strWhere = gd_implode(' AND ', $arrWhere);
$this->db->strJoin = DB_ADMIN_ORDER_GOODS_MEMO . ' AS aogm LEFT JOIN ' . DB_CODE . ' AS c ON c.itemCd = aogm.memoCd LEFT JOIN ' . DB_MANAGER . ' AS mn ON aogm.managerSno=mn.sno';
// 정렬 고정 — 정렬 UI 없음
$this->db->strOrder = 'aogm.regDt DESC';
 
 
 
 
$offset = $requestParams['page'] < 1 ? 0 : ($requestParams['page'] - 1) * $requestParams['pageNum'];
$this->db->strLimit = $offset . ',' . $requestParams['pageNum'];
 
Component/Order/OrderAdmin.php+44-12
변경 전OrderAdmin.php
const TEXT_PASSWORD_INVALID = '%s은(는) 비밀번호형식에 맞지 않습니다.';
const NOT_REQUEST_ORDER_STATUS = '해당 주문단계에서는 신청을 하실 수 없습니다.';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
protected $orderGridConfigList; //주문그리드 디폴트 설정 항목
protected $naverpayConfig; //네이버페이 설정
protected $paycoConfig; //페이코쇼핑 설정
* 관리자 주문 리스트
* 반품/교환/환불 정보까지 한번에 가져올 수 있게 되어있다.
*
* @param string $searchData 검색 데이터
* @param string $searchPeriod 기본 조회 기간
* @param boolean $isUserHandle 사용자 교환/반품/환불 신청 여부
*
$page = \App::load('\\Component\\Page\\Page', $searchData['page'], 0, 0, $searchData['pageNum']);
$page->setCache(true)->setUrl(\Request::getQueryString()); // 페이지당 리스트 수
 
 
 
 
 
 
// 주문상태 정렬 예외 케이스 처리
if ($searchData['sort'] == 'og.orderStatus asc') {
$searchData['sort'] = 'case LEFT(og.orderStatus, 1) when \'o\' then \'01\' when \'p\' then \'02\' when \'g\' then \'03\' when \'d\' then \'04\' when \'s\' then \'05\' when \'e\' then \'06\' when \'b\' then \'07\' when \'r\' then \'08\' when \'c\' then \'09\' when \'f\' then \'10\' else \'11\' end';
* 관리자앱 for 고도몰5 주문 리스트
* 반품/교환/환불 정보까지 한번에 가져오거나 상태별로 가져올 수 있게 되어있다.
*
* @param string $searchData 검색 데이터
* @param string $searchPeriod 기본 조회 기간
* @param boolean $isUserHandle 사용자 교환/반품/환불 신청 여부
*
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// 정렬 설정
$orderSort = gd_isset($searchData['sort'], 'o.regDt desc');
 
 
 
 
// 사용 필드
$arrIncludeOg = [
$this->search['keyword'] = gd_isset($request['keyword']);
$this->search['useFl'] = gd_isset($request['useFl']);
$this->search['sort'] = gd_isset($request['sort'], 'regDt desc');
 
 
 
 
$this->search['searchKind'] = gd_isset($request['searchKind']);
 
$this->checked['useFl'][$this->search['useFl']] = 'checked="checked"';
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// --- 정렬 설정
$orderSort = gd_isset($searchData['sort'], 'oi.regDt desc, oi.scmNo asc, oi.orderGoodsNo asc');
 
 
 
 
 
// 추출 필드
$arrIncludeOi = [
$this->search['sortList'] = [
'ofa.regDt desc' => sprintf('%s↓', __('등록일')),
'ofa.regDt asc' => sprintf('%s↑', __('등록일')),
'ofa.group desc' => sprintf('%s↓', __('그룹')),
'ofa.group asc' => sprintf('%s↑', __('그룹')),
'ofa.name desc' => sprintf('%s↓', __('이름')),
'ofa.name asc' => sprintf('%s↑', __('이름')),
];
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][0], date('Y-m-d', strtotime('-' . $this->search['periodFl'] . ' day')));
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][1], date('Y-m-d'));
$this->search['sort'] = gd_isset($searchData['sort'], gd_array_keys($this->search['sortList'])[0]);
 
 
 
 
$setData['search'] = $this->search;
 
// 체크 설정
$this->db->strField = ' aogm.*, mn.managerId, mn.managerNm, c.itemNm ';
$this->db->strWhere = gd_implode(' AND ', $arrWhere);
$this->db->strJoin = DB_ADMIN_ORDER_GOODS_MEMO . ' AS aogm LEFT JOIN ' . DB_CODE . ' AS c ON c.itemCd = aogm.memoCd LEFT JOIN ' . DB_MANAGER . ' AS mn ON aogm.managerSno=mn.sno';
if($requestParams['sort']) {
$this->db->strOrder = $requestParams['sort'];
} else {
$this->db->strOrder = 'aogm.regDt DESC';
}
 
$offset = $requestParams['page'] < 1 ? 0 : ($requestParams['page'] - 1) * $requestParams['pageNum'];
$this->db->strLimit = $offset . ',' . $requestParams['pageNum'];
 
변경 후OrderAdmin.php
const TEXT_PASSWORD_INVALID = '%s은(는) 비밀번호형식에 맞지 않습니다.';
const NOT_REQUEST_ORDER_STATUS = '해당 주문단계에서는 신청을 하실 수 없습니다.';
 
// 정렬 화이트리스트 (SQL Injection 방지)
const ALLOWED_SORT_ORDER = [
'o.regDt desc', 'o.regDt asc', // 주문일
'og.orderNo desc', 'og.orderNo asc', // 주문번호
'o.orderGoodsNm desc', 'o.orderGoodsNm asc', // 상품명
'oi.orderName desc', 'oi.orderName asc', // 주문자
'o.settlePrice desc', 'o.settlePrice asc', // 총 결제금액
'oi.receiverName desc', 'oi.receiverName asc', // 수령자
'sm.companyNm desc', 'sm.companyNm asc', // 공급사
'og.orderStatus desc', 'og.orderStatus asc', // 처리상태
'og.paymentDt desc', 'og.paymentDt asc', // 결제일
'oi.packetCode desc', 'oi.packetCode asc', // 묶음배송
'ouh.regDt desc', 'ouh.regDt asc', // 신청일
];
 
protected $orderGridConfigList; //주문그리드 디폴트 설정 항목
protected $naverpayConfig; //네이버페이 설정
protected $paycoConfig; //페이코쇼핑 설정
* 관리자 주문 리스트
* 반품/교환/환불 정보까지 한번에 가져올 수 있게 되어있다.
*
* @param array $searchData 검색 데이터
* @param string $searchPeriod 기본 조회 기간
* @param boolean $isUserHandle 사용자 교환/반품/환불 신청 여부
*
$page = \App::load('\\Component\\Page\\Page', $searchData['page'], 0, 0, $searchData['pageNum']);
$page->setCache(true)->setUrl(\Request::getQueryString()); // 페이지당 리스트 수
 
// 정렬 화이트리스트 검증 — SQL Injection 방지 (CASE WHEN 치환 전에 검증)
if (!empty($searchData['sort']) && !in_array($searchData['sort'], self::ALLOWED_SORT_ORDER, true)) {
$searchData['sort'] = '';
}
 
// 주문상태 정렬 예외 케이스 처리
if ($searchData['sort'] == 'og.orderStatus asc') {
$searchData['sort'] = 'case LEFT(og.orderStatus, 1) when \'o\' then \'01\' when \'p\' then \'02\' when \'g\' then \'03\' when \'d\' then \'04\' when \'s\' then \'05\' when \'e\' then \'06\' when \'b\' then \'07\' when \'r\' then \'08\' when \'c\' then \'09\' when \'f\' then \'10\' else \'11\' end';
* 관리자앱 for 고도몰5 주문 리스트
* 반품/교환/환불 정보까지 한번에 가져오거나 상태별로 가져올 수 있게 되어있다.
*
* @param array $searchData 검색 데이터
* @param string $searchPeriod 기본 조회 기간
* @param boolean $isUserHandle 사용자 교환/반품/환불 신청 여부
*
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$orderSort = gd_isset($searchData['sort'], 'o.regDt desc');
if (!in_array($orderSort, self::ALLOWED_SORT_ORDER, true)) {
$orderSort = 'o.regDt desc';
}
 
// 사용 필드
$arrIncludeOg = [
$this->search['keyword'] = gd_isset($request['keyword']);
$this->search['useFl'] = gd_isset($request['useFl']);
$this->search['sort'] = gd_isset($request['sort'], 'regDt desc');
// 정렬 화이트리스트 검증 — SQL Injection 방지
if (!array_key_exists($this->search['sort'], $this->search['sortList'])) {
$this->search['sort'] = 'regDt desc';
}
$this->search['searchKind'] = gd_isset($request['searchKind']);
 
$this->checked['useFl'][$this->search['useFl']] = 'checked="checked"';
$page->setPage();
$page->setUrl(\Request::getQueryString());
 
// --- 정렬 설정 (화이트리스트 검증 — SQL Injection 방지)
$defaultInvoiceSort = 'oi.regDt desc, oi.scmNo asc, oi.orderGoodsNo asc';
$orderSort = gd_isset($searchData['sort'], $defaultInvoiceSort);
if (!in_array($orderSort, self::ALLOWED_SORT_ORDER, true) && $orderSort !== $defaultInvoiceSort) {
$orderSort = $defaultInvoiceSort;
}
 
// 추출 필드
$arrIncludeOi = [
$this->search['sortList'] = [
'ofa.regDt desc' => sprintf('%s↓', __('등록일')),
'ofa.regDt asc' => sprintf('%s↑', __('등록일')),
'ofa.groupNm desc' => sprintf('%s↓', __('그룹')),
'ofa.groupNm asc' => sprintf('%s↑', __('그룹')),
'ofa.name desc' => sprintf('%s↓', __('이름')),
'ofa.name asc' => sprintf('%s↑', __('이름')),
];
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][0], date('Y-m-d', strtotime('-' . $this->search['periodFl'] . ' day')));
$this->search['treatDate'][] = gd_isset($searchData['treatDate'][1], date('Y-m-d'));
$this->search['sort'] = gd_isset($searchData['sort'], gd_array_keys($this->search['sortList'])[0]);
// 정렬 화이트리스트 검증 — SQL Injection 방지
if (!array_key_exists($this->search['sort'], $this->search['sortList'])) {
$this->search['sort'] = array_keys($this->search['sortList'])[0];
}
$setData['search'] = $this->search;
 
// 체크 설정
$this->db->strField = ' aogm.*, mn.managerId, mn.managerNm, c.itemNm ';
$this->db->strWhere = gd_implode(' AND ', $arrWhere);
$this->db->strJoin = DB_ADMIN_ORDER_GOODS_MEMO . ' AS aogm LEFT JOIN ' . DB_CODE . ' AS c ON c.itemCd = aogm.memoCd LEFT JOIN ' . DB_MANAGER . ' AS mn ON aogm.managerSno=mn.sno';
// 정렬 고정 — 정렬 UI 없음
$this->db->strOrder = 'aogm.regDt DESC';
 
 
 
 
$offset = $requestParams['page'] < 1 ? 0 : ($requestParams['page'] - 1) * $requestParams['pageNum'];
$this->db->strLimit = $offset . ',' . $requestParams['pageNum'];
 
상품 검색 보안 강화.
Component/Goods/Goods.php+14-1
변경 전Goods.php
*/
const RECENT_KEYWORD_COOKIE_DAYS = 365;
 
 
 
 
 
 
protected $db;
 
protected $arrBind = [];
*
* @return array 상품 정보
*/
public function getGoodsSearchList($pageNum = 10, $displayOrder = 'g.regDt asc', $imageType = 'list', $optionFl = false, $soldOutFl = true, $brandFl = false, $couponPriceFl = false, $displayCnt = 10, $brandDisplayFl = false, $usePage = true, $limit = null,array $goodsNo = null)
{
gd_isset($this->goodsTable,DB_GOODS);
$arrBind = null;
$mallBySession = SESSION::get(SESSION_GLOBAL_MALL);
 
 
 
 
 
 
 
 
 
$getValue = Request::get()->toArray();
 
 
변경 후Goods.php
*/
const RECENT_KEYWORD_COOKIE_DAYS = 365;
 
/**
* 페이징 기본값
*/
const PAGE_NUM_DEFAULT_SIZE = 10;
 
protected $db;
 
protected $arrBind = [];
*
* @return array 상품 정보
*/
public function getGoodsSearchList($pageNum = self::PAGE_NUM_DEFAULT_SIZE, $displayOrder = 'g.regDt asc', $imageType = 'list', $optionFl = false, $soldOutFl = true, $brandFl = false, $couponPriceFl = false, $displayCnt = 10, $brandDisplayFl = false, $usePage = true, $limit = null,array $goodsNo = null)
{
gd_isset($this->goodsTable,DB_GOODS);
$arrBind = null;
$mallBySession = SESSION::get(SESSION_GLOBAL_MALL);
if (is_null($pageNum)) {
$pageNum = self::PAGE_NUM_DEFAULT_SIZE;
}
 
$pageNum = filter_var($pageNum, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
if ($pageNum === false) {
throw new \InvalidArgumentException("pageNum이 정상적인 값이 아닙니다.");
}
 
$getValue = Request::get()->toArray();
 
 
상품 추천 기능 보안 강화.
Controller/Mobile/Goods/GoodsRecomController.php+3-3
변경 전GoodsRecomController.php
 
use Framework\Debug\Exception\AlertBackException;
use Framework\Debug\Exception\AlertOnlyException;
 
use Framework\Debug\Exception\Framework\Debug\Exception;
use Framework\Utility\DateTimeUtils;
use Message;
$this->setData('themeInfo', gd_isset($cateInfo));
$this->setData('gPageName', __("추천상품"));
 
 
 
} catch (\Exception $e) {
throw new AlertBackException($e->getMessage());
}
 
 
}
}
 
변경 후GoodsRecomController.php
 
use Framework\Debug\Exception\AlertBackException;
use Framework\Debug\Exception\AlertOnlyException;
use Framework\Debug\Exception\DatabaseException;
use Framework\Debug\Exception\Framework\Debug\Exception;
use Framework\Utility\DateTimeUtils;
use Message;
$this->setData('themeInfo', gd_isset($cateInfo));
$this->setData('gPageName', __("추천상품"));
 
} catch (DatabaseException $exception) {
throw new AlertBackException(__('잘못된 접근입니다.'));
} catch (\Exception $e) {
throw new AlertBackException($e->getMessage());
}
 
 
}
}
 
뱅크다 보안 강화.
Component/Bankda/BankdaGhostDepositor.php+3-20
변경 전BankdaGhostDepositor.php
throw new AlertCloseException('사용할 수 없습니다.');
}
 
$eval_json = array();
 
$_quoted = false;
$_out = '$eval_json=';
 
for ($i=0,$max=strlen($json); $i<$max; $i++) {
 
if (! $_quoted) {
if ($json[$i] == '{') $_out .= ' array(';
else if ($json[$i] == '}') $_out .= ')';
else if ($json[$i] == ':') $_out .= '=>';
else if ($json[$i] == '[') $_out .= ' array(';
else if ($json[$i] == ']') $_out .= ')';
else $_out .= $json[$i];
} else {
$_out .= $json[$i];
}
 
if ($json[$i] == '"') $_quoted = ! $_quoted;
}
 
@eval($_out.';');
 
$paging = '';
$loop = [];
$_page = [];
 
변경 후BankdaGhostDepositor.php
throw new AlertCloseException('사용할 수 없습니다.');
}
 
$eval_json = json_decode($json, true);
 
if (is_array($eval_json) === false) {
$eval_json = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}
 
 
 
$paging = '';
$loop = [];
$_page = [];
 
에러 로그 처리기(ErrorProcessor) 도입 및 LogAndCrash 적재 포맷 개선
Component/Attendance/AttendanceCheckLogin.php+4
변경 전AttendanceCheckLogin.php
 
use Bundle\Component\Attendance\Exception\AttendanceValidationException;
use Bundle\Component\Attendance\Exception\AttendanceGlobalMallException;
 
use Component\Attendance\Attendance;
use Framework\Object\SimpleStorage;
 
$exceptionMessage = __METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage();
$logger->warning($exceptionMessage, $e->getTrace());
throw $e;
 
 
 
} catch (\Exception $e) {
$exceptionMessage = __METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage();
$logger->error($exceptionMessage, $e->getTrace());
 
변경 후AttendanceCheckLogin.php
 
use Bundle\Component\Attendance\Exception\AttendanceValidationException;
use Bundle\Component\Attendance\Exception\AttendanceGlobalMallException;
use Framework\Debug\Exception\NoticeException;
use Component\Attendance\Attendance;
use Framework\Object\SimpleStorage;
 
$exceptionMessage = __METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage();
$logger->warning($exceptionMessage, $e->getTrace());
throw $e;
} catch (NoticeException $e) {
// 사용자 알림 예외는 error 적재 대상이 아님 — 아래 generic catch보다 먼저 rethrow
throw $e;
} catch (\Exception $e) {
$exceptionMessage = __METHOD__ . ', ' . $e->getFile() . '[' . $e->getLine() . '], ' . $e->getMessage();
$logger->error($exceptionMessage, $e->getTrace());
 
Component/Mail/MailMimeAuto.php+5-1
변경 전MailMimeAuto.php
}
} catch (Exception $e) {
$this->isAutoSend = false;
$this->logger->error($e->getMessage(), $e->getTrace());
 
 
 
 
}
$this->replaceData = $this->replaceCode->getReplaceCode();
$this->logger->info('Initialize end.');
 
변경 후MailMimeAuto.php
}
} catch (Exception $e) {
$this->isAutoSend = false;
if ($e->getCode() === 200) {
$this->logger->warning($e->getMessage(), $e->getTrace());
} else {
$this->logger->error($e->getMessage(), $e->getTrace());
}
}
$this->replaceData = $this->replaceCode->getReplaceCode();
$this->logger->info('Initialize end.');
 
Component/Mobile/MobileShop.php+2-2
변경 전MobileShop.php
if ($request->getQueryString()) {
$_tmp['retUrl'] = $_tmp['retUrl'] . '?' . $request->getQueryString();
}
$logger->alert(sprintf('%s, move index location[%s]', __METHOD__, $_tmp['retUrl']));
header('location:' . $_tmp['retUrl']);
exit();
}
if ($request->getQueryString()) {
$_tmp['retUrl'] = $_tmp['retUrl'] . '?' . $request->getQueryString();
}
$logger->alert(sprintf('%s, move mobile page location[%s]', __METHOD__, $_tmp['retUrl']));
header('location:' . $_tmp['retUrl']);
exit();
}
 
변경 후MobileShop.php
if ($request->getQueryString()) {
$_tmp['retUrl'] = $_tmp['retUrl'] . '?' . $request->getQueryString();
}
$logger->info(sprintf('%s, move index location[%s]', __METHOD__, $_tmp['retUrl']));
header('location:' . $_tmp['retUrl']);
exit();
}
if ($request->getQueryString()) {
$_tmp['retUrl'] = $_tmp['retUrl'] . '?' . $request->getQueryString();
}
$logger->info(sprintf('%s, move mobile page location[%s]', __METHOD__, $_tmp['retUrl']));
header('location:' . $_tmp['retUrl']);
exit();
}
 
고도몰 어드민 메인의 운영필수서비스 및 부가서비스 페이지 개선
Admin/base/_index_service_state.php+4-5
변경 전_index_service_state.php
<?php if (gd_is_provider() === false) { ?>
<div class="main-section">
<div class="table-title">
<span class="">운영필수서비스 현황</span>
</div>
<div class="slide-items-wrap">
<div class="single-item">
<div class="slide-item">
<img src="<?= PATH_ADMIN_GD_SHARE ?>img/icon_new.png">
<span class="slide-item-text">
<a href="/service/service_info.php?menu=consulting_livefeed_info">실시간으로 응답하라! 1:1 상담채팅</a>
</span>
</div>
</div>
</div>
 
<script>
// 운영필수서비스 현황
$('.single-item').slick();
</script>
</div>
<div class="main-section">
<div class="table-title">
부가서비스
</div>
<div class="plusshop-stat-outer">
<table class="table table-bordered">
 
변경 후_index_service_state.php
<?php if (gd_is_provider() === false) { ?>
<div class="main-section">
<div class="table-title">
<span class="">스토어</span>
</div>
<div class="slide-items-wrap">
<div class="single-item">
<div class="slide-item">
 
<span class="slide-item-text">
<a href="/service/service_info.php?menu=power_main">개발 없이 필요한 기능을 도입하세요.</a>
</span>
</div>
</div>
</div>
 
<script>
// 스토어
$('.single-item').slick();
</script>
</div>
<div class="main-section">
<div class="table-title">
운영필수서비스 현황
</div>
<div class="plusshop-stat-outer">
<table class="table table-bordered">