diff --git a/app/Config/Auth.php b/app/Config/Auth.php index 99c8f94..194e329 100644 --- a/app/Config/Auth.php +++ b/app/Config/Auth.php @@ -7,17 +7,19 @@ use CodeIgniter\Config\BaseConfig; /** * 로그인·2차 인증(TOTP) 관련 설정 * - * .env 예: - * auth.requireTotp = true - * auth.totpIssuer = "쓰레기봉투 물류시스템" + * .env 의 auth.requireTotp 가 Config 기본값보다 우선합니다. 끄려면 반드시 false 로 두세요. + * 예: + * auth.requireTotp = false + * auth.requireTotp = true # 운영에서 2FA 켤 때 + * auth.totpIssuer = "종량제 시스템" */ class Auth extends BaseConfig { - /** 운영·스테이징 true 권장. 로컬 개발 시 false 로 1단계만 로그인 가능 */ - public bool $requireTotp = true; + /** false 이면 로그인 시 TOTP·등록 유도 없음. 운영에서 켤 때 .env 에 auth.requireTotp = true */ + public bool $requireTotp = false; /** 인증 앱에 표시되는 발급자(issuer) */ - public string $totpIssuer = '쓰레기봉투 물류시스템'; + public string $totpIssuer = '종량제 시스템'; /** TOTP 연속 실패 시 세션 종료 전 허용 횟수 */ public int $totpMaxAttempts = 5; diff --git a/app/Config/Encryption.php b/app/Config/Encryption.php index 6eaf547..48dbbba 100644 --- a/app/Config/Encryption.php +++ b/app/Config/Encryption.php @@ -27,8 +27,40 @@ class Encryption extends BaseConfig public function __construct() { parent::__construct(); - $hex = (string) env('encryption.key', ''); + $hex = trim((string) env('encryption.key', '')); + if ( + (str_starts_with($hex, "'") && str_ends_with($hex, "'")) + || (str_starts_with($hex, '"') && str_ends_with($hex, '"')) + ) { + $hex = substr($hex, 1, -1); + } $this->key = (strlen($hex) === 64 && ctype_xdigit($hex)) ? hex2bin($hex) : ''; + + $prev = trim((string) env('encryption.previousKeys', '')); + if ($prev !== '') { + $parsed = []; + $parts = array_map('trim', explode(',', $prev)); + foreach ($parts as $part) { + if ($part === '') { + continue; + } + if (str_starts_with($part, 'hex2bin:')) { + $part = substr($part, 8); + } + if ( + (str_starts_with($part, "'") && str_ends_with($part, "'")) + || (str_starts_with($part, '"') && str_ends_with($part, '"')) + ) { + $part = substr($part, 1, -1); + } + if (strlen($part) === 64 && ctype_xdigit($part)) { + $parsed[] = 'hex2bin:' . $part; + } + } + if (! empty($parsed)) { + $this->previousKeys = $parsed; + } + } } /** diff --git a/app/Helpers/pii_encryption_helper.php b/app/Helpers/pii_encryption_helper.php index e4c13c3..86cc89c 100644 --- a/app/Helpers/pii_encryption_helper.php +++ b/app/Helpers/pii_encryption_helper.php @@ -8,6 +8,7 @@ declare(strict_types=1); * * 저장 형식: 암호화된 값은 "ENC:" + base64(암호문) 으로 저장. "ENC:" 없으면 평문(기존)으로 간주. */ + if (! function_exists('pii_encrypt')) { function pii_encrypt(?string $value): string { @@ -21,9 +22,8 @@ if (! function_exists('pii_encrypt')) { } $encrypter = service('encrypter'); $encrypted = $encrypter->encrypt($value); - return 'ENC:' . base64_encode($encrypted); - } catch (Throwable) { + } catch (Throwable $e) { return $value; } } @@ -44,13 +44,23 @@ if (! function_exists('pii_decrypt')) { return $value; } $encrypter = service('encrypter'); - $raw = base64_decode(substr($value, 4), true); - if ($raw === false) { - return $value; + $payload = substr($value, 4); + + // 현재 포맷: ENC: + base64(raw ciphertext) + $raw = base64_decode($payload, true); + if ($raw !== false) { + try { + return $encrypter->decrypt($raw); + } catch (Throwable $e) { + // legacy 포맷 재시도 + } } - return $encrypter->decrypt($raw); - } catch (Throwable) { + // 레거시 포맷 호환: + // - ENC: + encrypter 반환값(rawData=false 환경 등) 또는 + // - ENC: + 기타 문자열 포맷 + return $encrypter->decrypt($payload); + } catch (Throwable $e) { return $value; } } diff --git a/app/Views/admin/dashboard/index.php b/app/Views/admin/dashboard/index.php index 4171523..8a42f89 100644 --- a/app/Views/admin/dashboard/index.php +++ b/app/Views/admin/dashboard/index.php @@ -45,7 +45,7 @@
| - = esc($order->bo_lot_no) ?> + = esc($order->bo_lot_no) ?> | = esc($order->bo_order_date) ?> | @@ -90,7 +90,7 @@ |