본문으로 건너뛰기

선물하기 개인정보 보안 강화

PHP 8 · GODO26
변경 파일 수: 3개
변경 파일 목록
총 3개
변경 파일 코드
추가삭제
Component/Cart/Cart.php+182-4
변경 전Cart.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\NumberUtils;
use Framework\Utility\ProducerUtils;
 
 
use Origin\Service\Statistics\Analytics\Cart\CartAddHistoryAnalyticsCollectorService;
use Framework\Utility\SkinUtils;
use Framework\Utility\StringUtils;
const CART_GOODS_TYPE_GOODS = 'goods';
const CART_GOODS_TYPE_ADDGOODS = 'addGoods';
 
 
 
/**
* @var null|object 디비 접속
*/
}
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 장바구니 상품 삭제
*
$arrBind['param'] = 'sno IN (' . gd_implode(' , ', $param) . ') AND siteKey = ?';
$this->db->bind_param_push($arrBind['bind'], 's', $this->siteKey);
}
 
 
 
 
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
// 장바구니 갯수 변경 처리
// 장바구니 bind 데이터로 goodsNo 조회 - 갯수 변경 처리를 위해 추출
$changeGoodsNoArray = $this->checkCartSelectGoodsNo(null, $arrBind);
 
 
 
 
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
// 장바구니 갯수 변경 처리
$this->setDeleteDirectCart();
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 장바구니 유지기간 초과 상품 삭제
*/
public function setCartDeletePeriod()
{
if ($this->cartPolicy['periodFl'] === 'y' && $this->cartPolicy['periodDay'] > 0) {
$periodDate = date('Y-m-d H:i:s', strtotime('-' . $this->cartPolicy['periodDay'] . ' day'));
$arrBind['param'] = 'regDt < ?';
$this->db->bind_param_push($arrBind['bind'], 's', $periodDate);
// 장바구니 삭제 전 쿠폰 장바구니사용 상태 되돌리기
$cartCouponData = $this->getCartMemberCoupon($arrBind);
foreach ($cartCouponData as $cartKey => $cartVal) {
// 장바구니 bind 데이터로 goodsNo 조회 - 갯수 변경 처리를 위해 추출
$changeGoodsNoArray = $this->checkCartSelectGoodsNo(null, $arrBind);
 
 
 
 
// 장바구니 삭제
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
 
$strSQL = "SELECT * FROM " . $this->tableName . " WHERE siteKey = ? AND goodsNo = ? AND optionSno = ?" . $strDirectSQL . " ORDER BY directCart DESC, regDt ASC, modDt ASC";
$mergeList = $this->db->query_fetch($strSQL, $arrBind);
 
 
 
 
$tempCnt = 0;
$tempOptionText = '';
$tempAddNo = '';
 
$strSQL = "SELECT * FROM " . $this->tableName . " WHERE memNo = ? AND goodsNo = ? AND optionSno = ? AND optionText = ?" . $strDirectSQL . $isWriteAddSql . " ORDER BY directCart DESC, regDt ASC, modDt ASC";
$mergeList = $this->db->query_fetch($strSQL, $arrBind);
 
 
 
 
$tempCnt = 0;
$tempOptionText = '';
$tempAddNo = '';
 
변경 후Cart.php
use Framework\Utility\ArrayUtils;
use Framework\Utility\NumberUtils;
use Framework\Utility\ProducerUtils;
use Origin\Service\Cart\CartDeletePeriodPolicyService;
use Origin\Service\Present\PresentCartPrivacyDestroyService;
use Origin\Service\Statistics\Analytics\Cart\CartAddHistoryAnalyticsCollectorService;
use Framework\Utility\SkinUtils;
use Framework\Utility\StringUtils;
const CART_GOODS_TYPE_GOODS = 'goods';
const CART_GOODS_TYPE_ADDGOODS = 'addGoods';
 
const PRESENT_PRIVACY_DESTROY_LOG_SNO_LIMIT = 50;
 
/**
* @var null|object 디비 접속
*/
}
}
 
/**
* 삭제 조건에 해당하는 장바구니 sno 목록 조회
*
* @param array $arrBind where 조건 배열
*
* @return array 장바구니 sno 목록
*/
protected function getCartSnoListByBind($arrBind)
{
if (is_array($arrBind['param'])) {
$addWhere = implode(' AND ', $arrBind['param']);
} else {
$addWhere = $arrBind['param'];
}
 
$strSQL = 'SELECT sno FROM ' . $this->tableName . ' WHERE ' . $addWhere;
$cartData = $this->db->query_fetch($strSQL, $arrBind['bind'], true);
 
return array_column((array) $cartData, 'sno');
}
 
/**
* 현재 장바구니 테이블이 선물하기 수령자 개인정보 파기 대상인지 판정
*
* 판정 자체는 Origin 정책이 소유한다. 여기서 테이블명을 직접 비교하면 상점 튜닝으로 가드를 바꿔
* 파기 대상을 조용히 넓히거나 좁힐 수 있다.
*
* @return bool 파기 대상 테이블 여부
*/
protected function isPresentCartPrivacyTable()
{
return \App::getInstance(PresentCartPrivacyDestroyService::class)
->isPrivacyLinkedCartTable($this->tableName);
}
 
/**
* 선물하기 수령자 개인정보(es_presentCart) 파기
*
* @param array $arrBind 삭제 대상 where 조건 배열
*
* @return int 파기 건수
*/
protected function destroyPresentCartPrivacy($arrBind)
{
$cartSnoList = [];
try {
// 파기 대상 테이블이 아니면 조회 자체를 하지 않는다
if (!$this->isPresentCartPrivacyTable()) {
return 0;
}
 
$cartSnoList = $this->getCartSnoListByBind($arrBind);
 
return $this->destroyPresentCartPrivacyBySnos($cartSnoList);
} catch (\Throwable $e) {
// 결제 완료 직후 장바구니 비우기가 이 경로를 타므로, 파기 실패로 사용자 동선을 끊지 않는다
$this->logPresentCartPrivacyDestroyFailure($cartSnoList, $e);
return 0;
}
}
 
/**
* 장바구니 sno 목록 기준 선물하기 수령자 개인정보 파기
*
* 삭제가 루프 안에서 건별로 일어나는 경로는 sno 를 먼저 모아 여기로 한 번에 넘겨야 N+1 이 되지 않는다.
*
* @param array $cartSnoList 삭제 대상 장바구니 sno 목록
*
* @return int 파기 건수
*/
protected function destroyPresentCartPrivacyBySnos($cartSnoList)
{
try {
if (empty($cartSnoList) || !$this->isPresentCartPrivacyTable()) {
return 0;
}
 
return \App::getInstance(PresentCartPrivacyDestroyService::class)->destroyByCartSnos($cartSnoList);
} catch (\Throwable $e) {
// 결제 완료 직후 장바구니 비우기가 이 경로를 타므로, 파기 실패로 사용자 동선을 끊지 않는다
$this->logPresentCartPrivacyDestroyFailure($cartSnoList, $e);
return 0;
}
}
 
/**
* 선물하기 수령자 개인정보 파기 실패 기록
*
* 실시간 사용자 경로는 파기 실패를 삼키므로, 이 로그가 잔존 개인정보를 되짚을 유일한 근거다.
* 대상이 대량일 수 있어 전체 목록 대신 건수와 앞부분만 남긴다.
*
* @param array $cartSnoList 파기 대상 장바구니 sno 목록
* @param \Throwable $e 파기 중 발생한 예외
*/
protected function logPresentCartPrivacyDestroyFailure($cartSnoList, $e)
{
$cartSnoList = (array) $cartSnoList;
 
\Logger::channel('cart')->error('선물하기 수령자 개인정보 파기 실패', [
'exception' => $e,
'errorMessage' => $e->getMessage(),
'cartTable' => $this->tableName,
'cartSnoCnt' => count($cartSnoList),
'cartSnos' => array_slice($cartSnoList, 0, self::PRESENT_PRIVACY_DESTROY_LOG_SNO_LIMIT),
]);
}
 
/**
* 장바구니 병합 시 삭제될 sno 목록 추출
*
* 병합 루프는 첫 레코드만 남기고 두 번째부터 삭제한다. 삭제 판정을 루프와 동일하게 맞춰야
* 실제로 지워지지 않을 장바구니의 개인정보까지 파기하지 않는다.
*
* @param array $mergeList 병합 대상 장바구니 목록
*
* @return array 삭제 대상 장바구니 sno 정수 배열
*/
protected function getMergeDeleteCartSnoList($mergeList)
{
$deleteCartSnoList = [];
foreach ((array) $mergeList as $mergeKey => $mergeVal) {
if ($mergeVal['optionText']) {
continue;
}
 
if ($mergeKey > 0) {
$deleteCartSnoList[] = (int) $mergeVal['sno'];
}
}
 
return $deleteCartSnoList;
}
 
/**
* 장바구니 상품 삭제
*
$arrBind['param'] = 'sno IN (' . gd_implode(' , ', $param) . ') AND siteKey = ?';
$this->db->bind_param_push($arrBind['bind'], 's', $this->siteKey);
}
 
// 개인정보를 먼저 파기한 뒤 장바구니 본체를 삭제한다
$this->destroyPresentCartPrivacy($arrBind);
 
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
// 장바구니 갯수 변경 처리
// 장바구니 bind 데이터로 goodsNo 조회 - 갯수 변경 처리를 위해 추출
$changeGoodsNoArray = $this->checkCartSelectGoodsNo(null, $arrBind);
 
// 개인정보를 먼저 파기한 뒤 장바구니 본체를 삭제한다
$this->destroyPresentCartPrivacy($arrBind);
 
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
// 장바구니 갯수 변경 처리
$this->setDeleteDirectCart();
}
 
/**
* 장바구니 유지기간 초과 삭제 조건 조회
*
* 만료 기준 산출은 Origin 정책 서비스가 소유한다. 선행 파기와 실제 삭제가 같은 기준시각을 쓰는 것은
* 정책 서비스의 메모이즈로 보장되므로 여기서 기준시각을 다시 계산하면 안 된다.
*
* @return array 유지기간 정책이 꺼져 있으면 빈 배열, 켜져 있으면 where 조건 배열
*/
protected function getCartDeletePeriodBind()
{
// gd_policy 는 설정이 없으면 배열이 아닌 값을 돌려주므로 정책 서비스에 그대로 넘기지 않는다
$cartPolicy = is_array($this->cartPolicy) ? $this->cartPolicy : null;
 
$expiryDate = \App::getInstance(CartDeletePeriodPolicyService::class)->getExpiryDate($cartPolicy);
if ($expiryDate === null) {
return [];
}
 
$arrBind = [];
$arrBind['param'] = 'regDt < ?';
$this->db->bind_param_push($arrBind['bind'], 's', $expiryDate);
 
return $arrBind;
}
 
/**
* 장바구니 유지기간 초과 상품 삭제
*/
public function setCartDeletePeriod()
{
$arrBind = $this->getCartDeletePeriodBind();
if (!empty($arrBind)) {
 
 
// 장바구니 삭제 전 쿠폰 장바구니사용 상태 되돌리기
$cartCouponData = $this->getCartMemberCoupon($arrBind);
foreach ($cartCouponData as $cartKey => $cartVal) {
// 장바구니 bind 데이터로 goodsNo 조회 - 갯수 변경 처리를 위해 추출
$changeGoodsNoArray = $this->checkCartSelectGoodsNo(null, $arrBind);
 
// 개인정보를 먼저 파기한 뒤 장바구니 본체를 삭제한다
$this->destroyPresentCartPrivacy($arrBind);
 
// 장바구니 삭제
$this->db->set_delete_db($this->tableName, $arrBind['param'], $arrBind['bind']);
 
 
$strSQL = "SELECT * FROM " . $this->tableName . " WHERE siteKey = ? AND goodsNo = ? AND optionSno = ?" . $strDirectSQL . " ORDER BY directCart DESC, regDt ASC, modDt ASC";
$mergeList = $this->db->query_fetch($strSQL, $arrBind);
 
// 병합 루프가 건별로 지우므로, 개인정보는 루프 진입 전에 일괄로 먼저 파기한다
$this->destroyPresentCartPrivacyBySnos($this->getMergeDeleteCartSnoList($mergeList));
 
$tempCnt = 0;
$tempOptionText = '';
$tempAddNo = '';
 
$strSQL = "SELECT * FROM " . $this->tableName . " WHERE memNo = ? AND goodsNo = ? AND optionSno = ? AND optionText = ?" . $strDirectSQL . $isWriteAddSql . " ORDER BY directCart DESC, regDt ASC, modDt ASC";
$mergeList = $this->db->query_fetch($strSQL, $arrBind);
 
// 병합 루프가 건별로 지우므로, 개인정보는 루프 진입 전에 일괄로 먼저 파기한다
$this->destroyPresentCartPrivacyBySnos($this->getMergeDeleteCartSnoList($mergeList));
 
$tempCnt = 0;
$tempOptionText = '';
$tempAddNo = '';
 
Component/Goods/GoodsAdmin.php+21
변경 전GoodsAdmin.php
use Framework\Utility\ProducerUtils;
use Framework\Utility\StringUtils;
use Origin\Service\Marketing\Google\GoogleGoodsFeedService;
 
use Origin\Service\WebHook\GoodsWebHookService;
use Globals;
use LogHandler;
$this->deleteGoodsOptionObsImage($goodsNo);
}
 
 
 
 
foreach ($arrGoodsTable as $goodsTableNm) {
if ($goodsTableNm == DB_SEO_TAG) {
$this->db->set_delete_db($goodsTableNm, 'pageCode = ?', $this->arrBind);
$goodsWebHookService->sendGoodsWebHook($goodsNo, 'DELETED');
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/**
* 상품의 OBS 이미지 제거
* @param $goodsNo
 
변경 후GoodsAdmin.php
use Framework\Utility\ProducerUtils;
use Framework\Utility\StringUtils;
use Origin\Service\Marketing\Google\GoogleGoodsFeedService;
use Origin\Service\Present\PresentCartPrivacyDestroyService;
use Origin\Service\WebHook\GoodsWebHookService;
use Globals;
use LogHandler;
$this->deleteGoodsOptionObsImage($goodsNo);
}
 
// 상품 삭제는 장바구니를 통째로 지우므로, 삭제 테이블 루프보다 먼저 개인정보를 파기한다
$this->destroyPresentCartPrivacyByGoodsNo($goodsNo);
 
foreach ($arrGoodsTable as $goodsTableNm) {
if ($goodsTableNm == DB_SEO_TAG) {
$this->db->set_delete_db($goodsTableNm, 'pageCode = ?', $this->arrBind);
$goodsWebHookService->sendGoodsWebHook($goodsNo, 'DELETED');
}
 
/**
* 삭제 상품의 장바구니에 연결된 선물하기 수령자 개인정보 파기
*
* 상품 완전삭제는 es_cart 를 goodsNo 기준으로 통째로 지우므로, 파기하지 않으면 es_presentCart 의
* 수령자 개인정보가 참조할 장바구니 없이 영구히 남는다.
*
* 파기 대상 조회는 Origin 서비스가 소유한다. 여기서 직접 조회하면 상점 튜닝으로 대상이 좁혀질 수 있다.
*
* @param integer $goodsNo 상품번호
*
* @return int 파기 건수
*/
protected function destroyPresentCartPrivacyByGoodsNo($goodsNo)
{
return App::getInstance(PresentCartPrivacyDestroyService::class)->destroyByGoodsNo((int) $goodsNo);
}
 
/**
* 상품의 OBS 이미지 제거
* @param $goodsNo
 
Component/Member/Member.php+5
변경 전Member.php
use Bundle\Component\Captcha\Captcha;
use Bundle\Component\Godo\GodoKakaoServerApi;
use Origin\Service\Captcha\AltchaService;
 
use Bundle\Component\Policy\KakaoLoginPolicy;
use Component\Database\DBTableField;
use Component\Member\Company\CompanyCertification;
'i',
$memNo,
];
 
 
 
 
$this->db->set_delete_db(DB_MEMBER, 'memNo = ?', $arrBind);
$this->db->set_delete_db(DB_MEMBER_MILEAGE, 'memNo = ?', $arrBind);
}
 
변경 후Member.php
use Bundle\Component\Captcha\Captcha;
use Bundle\Component\Godo\GodoKakaoServerApi;
use Origin\Service\Captcha\AltchaService;
use Origin\Service\Member\MemberHackOutPrivacyDestroyService;
use Bundle\Component\Policy\KakaoLoginPolicy;
use Component\Database\DBTableField;
use Component\Member\Company\CompanyCertification;
'i',
$memNo,
];
 
// 개인정보를 먼저 파기한 뒤 회원 본체를 삭제한다
\App::getInstance(MemberHackOutPrivacyDestroyService::class)->destroyByMemNo((int) $memNo);
 
$this->db->set_delete_db(DB_MEMBER, 'memNo = ?', $arrBind);
$this->db->set_delete_db(DB_MEMBER_MILEAGE, 'memNo = ?', $arrBind);
}