FHM31052601IBL - script masking kolom plaintext PII m_patient & m_patientaddress

Semua 300+ controller otomatis tampilkan data termasking tanpa perlu
diupdate satu-satu. Data asli tetap aman di kolom _enc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sas.fajri
2026-05-31 14:47:29 +07:00
parent 6c0394aea3
commit e990609523

View File

@@ -0,0 +1,169 @@
<?php
/**
* Masking kolom plaintext PII di m_patient dan m_patientaddress
* Data asli tetap aman di kolom _enc
* Jalankan via: php scripts/mask_patient_plaintext.php
* Aman dijalankan berulang: skip row yang sudah termasking
*/
define('BASEPATH', true);
foreach (file(__DIR__ . '/../.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $l) {
if (strpos(trim($l), '#') === 0) continue;
[$k, $v] = array_map('trim', explode('=', $l, 2));
if ($k !== '') $_ENV[$k] = $v;
}
include __DIR__ . '/../application/config/database.php';
$cfg = $db['default'];
$pdo = new PDO(
"mysql:host={$cfg['hostname']};dbname={$cfg['database']};charset=utf8",
$cfg['username'],
$cfg['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// Deteksi apakah sudah dimasking: cek apakah M_PatientName masih plaintext
// (plaintext = tidak ada '***', sudah masked = ada '***')
function is_masked($val) {
return $val === null || strpos($val, '***') !== false;
}
function mask_name($v) {
if (!$v) return $v;
$v = trim($v);
$len = mb_strlen($v, 'UTF-8');
if ($len <= 2) return '***';
return mb_substr($v, 0, 2, 'UTF-8') . str_repeat('*', min(3, $len - 2));
}
function mask_phone($v) {
if (!$v) return $v;
$digits = preg_replace('/[^0-9]/', '', $v);
$len = strlen($digits);
if ($len <= 4) return '****';
if ($len <= 8) return substr($digits, 0, 4) . str_repeat('*', $len - 4);
return substr($digits, 0, 4) . str_repeat('*', $len - 7) . substr($digits, -3);
}
function mask_email($v) {
if (!$v || strpos($v, '@') === false) return $v;
[$local, $domain] = explode('@', $v, 2);
$show = mb_substr($local, 0, min(2, mb_strlen($local, 'UTF-8')), 'UTF-8');
return $show . '***@' . $domain;
}
function mask_short($v) {
if (!$v) return $v;
$v = trim($v);
$len = mb_strlen($v, 'UTF-8');
if ($len <= 2) return '***';
return mb_substr($v, 0, 2, 'UTF-8') . '***';
}
function mask_id($v) {
if (!$v) return $v;
$v = trim($v);
$len = strlen($v);
if ($len <= 4) return '****';
return substr($v, 0, 4) . str_repeat('*', max(3, $len - 6)) . ($len > 6 ? substr($v, -2) : '');
}
function mask_address($v) {
if (!$v) return $v;
$v = trim($v);
$len = mb_strlen($v, 'UTF-8');
if ($len <= 5) return '***';
return mb_substr($v, 0, 5, 'UTF-8') . '***';
}
$batch = 500;
// ============================================================
// m_patient
// ============================================================
echo "=== Masking m_patient ===\n";
$total = 0;
$stmt = $pdo->prepare("UPDATE m_patient SET
M_PatientName = ?,
M_PatientHP = ?,
M_PatientPhone = ?,
M_PatientEmail = ?,
M_PatientPOB = ?,
M_PatientIDNumber = ?,
M_PatientNIK = ?,
M_PatientNIP = ?
WHERE M_PatientID = ?");
while (true) {
// Skip rows already masked (ada '***' di nama)
$rows = $pdo->query(
"SELECT M_PatientID, M_PatientName, M_PatientHP, M_PatientPhone,
M_PatientEmail, M_PatientPOB, M_PatientIDNumber, M_PatientNIK, M_PatientNIP
FROM m_patient
WHERE M_PatientName_enc IS NOT NULL
AND M_PatientName NOT LIKE '%***%'
LIMIT {$batch}"
)->fetchAll(PDO::FETCH_ASSOC);
if (empty($rows)) break;
foreach ($rows as $row) {
$stmt->execute([
mask_name($row['M_PatientName']),
mask_phone($row['M_PatientHP']),
mask_phone($row['M_PatientPhone']),
mask_email($row['M_PatientEmail']),
mask_short($row['M_PatientPOB']),
mask_id($row['M_PatientIDNumber']),
mask_id($row['M_PatientNIK']),
mask_id($row['M_PatientNIP']),
$row['M_PatientID'],
]);
$total++;
}
echo " {$total} rows...\n";
}
echo "m_patient selesai: {$total} rows\n\n";
// ============================================================
// m_patientaddress
// ============================================================
echo "=== Masking m_patientaddress ===\n";
$total = 0;
$stmt2 = $pdo->prepare("UPDATE m_patientaddress SET
M_PatientAddressDescription = ?,
M_PatientAddressEmail = ?,
M_PatientAddressPhone = ?
WHERE M_PatientAddressID = ?");
while (true) {
$rows = $pdo->query(
"SELECT M_PatientAddressID, M_PatientAddressDescription,
M_PatientAddressEmail, M_PatientAddressPhone
FROM m_patientaddress
WHERE M_PatientAddressDescription_enc IS NOT NULL
AND (M_PatientAddressDescription IS NULL
OR M_PatientAddressDescription NOT LIKE '%***%')
LIMIT {$batch}"
)->fetchAll(PDO::FETCH_ASSOC);
if (empty($rows)) break;
foreach ($rows as $row) {
$stmt2->execute([
mask_address($row['M_PatientAddressDescription']),
mask_email($row['M_PatientAddressEmail']),
mask_phone($row['M_PatientAddressPhone']),
$row['M_PatientAddressID'],
]);
$total++;
}
echo " {$total} rows...\n";
}
echo "m_patientaddress selesai: {$total} rows\n\n";
echo "=== Masking plaintext selesai ===\n";
echo "Catatan: data asli tetap tersimpan aman di kolom _enc\n";