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>
170 lines
5.2 KiB
PHP
170 lines
5.2 KiB
PHP
<?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";
|