": AES-256-CBC with a random IV per value and * a key derived from APP_SECRET_KEY (config.php, from the deployment environment). * * Older values were encrypted with the fixed key $pinkey ("wms") and a constant IV, * which anyone reading the source can undo. secret_decrypt() still reads that legacy * format so existing rows keep working; setup.php re-encrypts them to v2 and every * save writes v2. Without APP_SECRET_KEY the legacy format is written (and logged) * so a deployment that has not set the key yet keeps sending mail. */ const SECRET_BOX_LEGACY_IV = '1234567890123456'; function secret_box_key(): ?string { if (!defined('APP_SECRET_KEY') || APP_SECRET_KEY === '') return null; return hash('sha256', APP_SECRET_KEY, true); } function secret_encrypt(string $plain, string $legacy_key = 'wms'): string { $key = secret_box_key(); if ($key === null) { error_log('[secret_box] APP_SECRET_KEY is not set; storing a credential in the legacy format.'); return openssl_encrypt($plain, 'AES-256-CBC', $legacy_key, 0, SECRET_BOX_LEGACY_IV); } $iv = random_bytes(16); $ct = openssl_encrypt($plain, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); return 'v2:' . base64_encode($iv . $ct); } /** Returns the plain text, or false when the value cannot be decrypted. */ function secret_decrypt(string $stored, string $legacy_key = 'wms') { $stored = trim($stored); if (strncmp($stored, 'v2:', 3) === 0) { $key = secret_box_key(); $raw = base64_decode(substr($stored, 3), true); if ($key === null || $raw === false || strlen($raw) <= 16) return false; return openssl_decrypt(substr($raw, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, substr($raw, 0, 16)); } return openssl_decrypt($stored, 'AES-256-CBC', $legacy_key, 0, SECRET_BOX_LEGACY_IV); } /** Whether a stored value still uses the legacy fixed-key format. */ function secret_is_legacy(string $stored): bool { return $stored !== '' && strncmp(trim($stored), 'v2:', 3) !== 0; }