encryptAtRest ?? false); } catch (\Throwable $e) { return false; } } /** @return list 암호화·마스킹 대상 PII 필드 */ private function piiFields(): array { try { $f = config('Pii')->designatedShopPiiFields ?? []; return is_array($f) && $f !== [] ? $f : ['ds_rep_name', 'ds_tel', 'ds_rep_phone', 'ds_email', 'ds_va_account']; } catch (\Throwable $e) { return ['ds_rep_name', 'ds_tel', 'ds_rep_phone', 'ds_email', 'ds_va_account']; } } /** 저장 전 PII 필드 암호화 (encryptAtRest ON일 때만, 이미 ENC:면 건너뜀) */ protected function piiEncrypt(array $data): array { if (! $this->encryptAtRest() || empty($data['data']) || ! is_array($data['data'])) { return $data; } helper('pii_encryption'); foreach ($this->piiFields() as $f) { if (array_key_exists($f, $data['data']) && $data['data'][$f] !== null && $data['data'][$f] !== '') { $v = (string) $data['data'][$f]; if (strpos($v, 'ENC:') !== 0) { $data['data'][$f] = pii_encrypt($v); } } } return $data; } /** 저장 전 블라인드 인덱스(전화·이름) 기록 — 컬럼과 인덱스키가 있을 때만 */ protected function piiBlindIndex(array $data): array { if (empty($data['data']) || ! is_array($data['data'])) { return $data; } helper('pii_encryption'); $db = $this->db; if ($this->telBidxExists === null) { $this->telBidxExists = $db->fieldExists('ds_tel_bidx', $this->table); } if ($this->nameBidxExists === null) { $this->nameBidxExists = $db->fieldExists('ds_rep_name_bidx', $this->table); } // 원본값이 넘어온 경우에만 인덱스 갱신(암호화 콜백이 먼저 돌면 ENC:라 인덱스 불가 → piiBlindIndex를 piiEncrypt보다 먼저 두지 않음에 주의) if ($this->telBidxExists && array_key_exists('ds_tel', $data['data'])) { $raw = (string) $data['data']['ds_tel']; $data['data']['ds_tel_bidx'] = strpos($raw, 'ENC:') === 0 ? '' : pii_blind_index($raw); } if ($this->nameBidxExists && array_key_exists('ds_rep_name', $data['data'])) { $raw = (string) $data['data']['ds_rep_name']; $data['data']['ds_rep_name_bidx'] = strpos($raw, 'ENC:') === 0 ? '' : pii_blind_index($raw); } return $data; } /** 조회 후 PII 필드 복호화 (ENC: 값만; 평문은 그대로) */ protected function piiDecrypt(array $data): array { if (empty($data['data'])) { return $data; } helper('pii_encryption'); $fields = $this->piiFields(); $decodeOne = static function ($row) use ($fields) { if (is_object($row)) { foreach ($fields as $f) { if (isset($row->$f) && is_string($row->$f) && strpos($row->$f, 'ENC:') === 0) { $row->$f = pii_decrypt($row->$f); } } } elseif (is_array($row)) { foreach ($fields as $f) { if (isset($row[$f]) && is_string($row[$f]) && strpos($row[$f], 'ENC:') === 0) { $row[$f] = pii_decrypt($row[$f]); } } } return $row; }; if (is_array($data['data'])) { // findAll 등: 결과 배열 foreach ($data['data'] as $i => $row) { $data['data'][$i] = $decodeOne($row); } } else { // find/first: 단일 $data['data'] = $decodeOne($data['data']); } return $data; } }