Data Masking — Alur Fungsional Current (Shifraud)

Dokumen ini adalah baseline perilaku current (source-traced) untuk fitur Data Masking: setup key kriptografi server, penerbitan dan rotasi Data Access Key user, konfigurasi dynamic sensitive field, proses masking response, dan unmasking filter search — beserta known gaps dan target perbaikan (Flow 4).


0. Prasyarat: Key Kriptografi Server

Data Masking tidak menggunakan pasangan public key/private key. Implementasi current menggunakan satu symmetric AES-256 key dengan alias tetap aes-key-01, diakses melalui KeyService, dengan algoritma AES/GCM/NoPadding. Key ini dipakai untuk:

Key server ini berbeda dari Data Access Key milik user.

[!WARNING] Jaga kesinambungan key. Ciphertext yang sudah tersimpan atau sudah diberikan ke client hanya dapat didekripsi dengan AES key yang sama. Jangan mengganti/menghapus key tanpa strategi re-enkripsi atau retensi key lama. Jangan mengirimkan AES key ke frontend/client.

0.1 Pilihan Setup

Pilih satu mekanisme berikut sebelum aplikasi dijalankan:

Mode Setup Perilaku current
Direkomendasikan: environment secret Set environment variable SHIFRAUD_AES_MASTER_KEY ke key AES-256 yang telah di-Base64-encode (hasil decode harus 32 byte). LocalKeyService memakai nilai ini untuk alias aes-key-01; key tidak dibuat sebagai file.
Local file fallback Set shifraud.security.key.path ke direktori aman yang persisten dan hanya dapat dibaca account service. Biarkan SHIFRAUD_AES_MASTER_KEY kosong. Saat startup, sistem membaca <path>/aes-key-01.key; jika belum ada, sistem otomatis membuat AES-256 key baru dan menyimpannya dalam Base64.
HSM (profile hsm) Aktifkan Spring profile hsm, konfigurasi hsm.library, hsm.slot, hsm.pin, dan opsional hsm.name; lalu provision secret key AES dengan alias aes-key-01 di HSM. HSMKeyService mengambil key tersebut melalui PKCS#11. Aplikasi gagal mengenkripsi/dekripsi bila alias tidak tersedia.

Contoh pembuatan secret AES-256 untuk mode environment (jalankan di mesin/secret-management workflow yang aman, lalu simpan output-nya sebagai secret SHIFRAUD_AES_MASTER_KEY):

openssl rand -base64 32

[!IMPORTANT]

0.2 Urutan Operasional

  1. Setup key kriptografi server memakai salah satu opsi 0.1; pastikan alias aes-key-01 tersedia sebelum backend start.
  2. Jalankan backend. Saat ApplicationReadyEvent, sistem membuat Data Access Key baru untuk user yang belum memilikinya/dummy key, lalu menyimpannya terenkripsi (ENC:<base64>) pada T_USER.key.
  3. User memperoleh Data Access Key. Panggil GET /user/find/{username}; response menyediakan raw token pada atribut key. Frontend/client menyimpannya secara aman dan mengirimkannya sebagai header X-Data-Access-Key pada endpoint masking. Token juga dapat diperbarui lewat event SSE saat rotasi harian.
  4. Admin menambahkan sensitive field lewat POST /sensitive-fields/add. Lihat kandidat field lewat GET /sensitive-fields/available; gunakan format addtData.<subKey> untuk field dalam map addtData.
  5. Mask response. Request tanpa header Data Access Key valid menerima field aktif dalam bentuk AES/GCM ciphertext (atau [PROTECTED] untuk nilai non-String/error). Request dengan key valid menerima plaintext.
  6. Unmask untuk search filter. Client yang menerima ciphertext dapat mengirim ciphertext tersebut kembali sebagai nilai filter; backend mendekripsinya sebelum query. Pada behavior current, unmask filter belum memvalidasi Data Access Key, dan plaintext apa adanya tetap diteruskan — lihat known gaps / Flow 4 untuk target hardening.

Status Verifikasi

Area Status Batas verifikasi
Service flow dan branching SOURCE-TRACED Ditelusuri dari TransactionMaskService dan dependensinya.
Endpoint dan header SOURCE-TRACED Ditelusuri dari penggunaan anotasi header X-Data-Access-Key di controllers.
Database behavior SOURCE-TRACED Berdasarkan token user dan config entity behavior.
Runtime behavior NOT RUNTIME-VERIFIED Tidak menjalankan aplikasi atau integration test.
Production behavior NOT PRODUCTION-VERIFIED Tidak memeriksa cron setting dan keys environment production.

Peta Komponen Utama

Concern Komponen current Tanggung jawab
Masking orchestration TransactionMaskService Menangkap map payload, menguji token, meloop active sensitive config, dan mengganti nilai menjadi cipher atau fallback.
Access token lifecycle DataAccessKeyService Menginisialisasi dummy key, memvalidasi input user vs database, menyimpan cache validasi, dan melakukan cron harian rotasi token.
Encryption engine EncryptionService Melakukan operasi kriptografi AES/GCM/NoPadding menggunakan active KeyService. Menelan error decrypt pada unmasking filter.
Sensitive fields registry SensitiveFieldConfigService Menyimpan field mana saja yang dianggap sensitif. Memvalidasi field baru terhadap domain class yang didukung.

Ringkasan End-to-End

[Diagram]

Flow 1 — Access Key Validation dan Lifecycle

Token (Data Access Key) setiap user disimpan dan dirotasi di dalam database secara otomatis. Mekanisme validasi memiliki fallback terhadap format key legacy.

1.1 Inisialisasi Key Saat Aplikasi Berjalan

Ketika aplikasi backend pertama kali berjalan (terpicu oleh ApplicationReadyEvent), DataAccessKeyService menjalankan initializeUserKeysOnStartup():

1.2 Cara User Mendapatkan Data Access Key

1.3 Validasi Token

Token divalidasi terhadap user terautentikasi (userId), bukan sekadar tokennya saja.

[Diagram]

Format T_USER.key yang didukung: ENC: (current), BCrypt legacy ($2a$), atau plaintext legacy.

[!DANGER] Cache invalidation delay. validationCache menggunakan ConcurrentHashMap dengan TTL 5 menit (CACHE_TTL_MS), namun current behavior tidak membersihkan cache spesifik user jika role atau token mereka dicabut (misalnya deactivate user) di pertengahan jalan, kecuali ketika global scheduled rotation berjalan. Token yang sudah ter-cache akan terus di-honor hingga 5 menit berlalu.

1.4 Rotasi Key (Key Rotation)

Proses rotasi token berjalan otomatis di belakang layar menggunakan Spring Scheduler:


Flow 2 — Konfigurasi Dynamic Sensitive Fields

SensitiveFieldConfigService mengatur field apa saja yang disembunyikan.

Operasi Endpoint Catatan
Lihat field aktif GET /sensitive-fields/ Membaca konfigurasi dari T_SENSITIVE_FIELD_CONFIG.
Lihat kandidat static field GET /sensitive-fields/available Kandidat berasal dari field ExistingCurrTrans yang belum didaftarkan.
Tambah field POST /sensitive-fields/add Body: { "fieldName": "...", "createdBy": "..." }; createdBy default system.
Hapus field DELETE /sensitive-fields/delete/{fieldName} Menghapus konfigurasi aktif.
[Diagram]

Nama field static harus berasal dari ExistingCurrTrans. Field dinamis hanya diterima dalam bentuk addtData.<subKey>, misalnya addtData.customerName.

[!DANGER] Tightly coupled static validation. Validasi field bergantung pada variabel statis VALID_FIELDS yang hanya me-load attributes dari ExistingCurrTrans.class.getDeclaredFields(). Jika app memiliki entity lain yang digunakan dalam response (misalnya AlertCase atau AcquirerAlert) dan entity tersebut memiliki nama field unik yang perlu di-mask, current behavior menolak registrasinya. Hanya field dengan nama sama dengan milik ExistingCurrTrans atau field dalam objek addtData yang bisa ditambahkan.


Flow 3 — Masking Data (Response Payload)

Jika token invalid atau absent, proses mutasi map data berjalan (replace original value). Controller yang telah terintegrasi meneruskan X-Data-Access-Key dan userId dari authentication ke TransactionMaskService, mencakup: existing-transaction (pencarian & detail), alert-acquirer & alert-issuer (pencarian & detail), dan case (pencarian).

[Diagram]

Alur ringkas per request:

  1. Service mengonversi entity/row menjadi Map.
  2. Key valid (isValid = true): data dikembalikan tanpa perubahan (plaintext).
  3. Key kosong, tidak valid, atau validasi error: setiap active sensitive field dimasking.

[!DANGER] Type mismatch di JSON payload. Jika field yang dikonfigurasi sensitif aslinya berjenis Number atau Boolean (contoh: amount, isFraud), current behavior memaksakan field tersebut menjadi String "[PROTECTED]". Ini langsung mengubah skema response API (dari integer ke string) dan berpotensi memunculkan error unmarshal/deserialization pada UI/client.

[!INFO] Grouped pagination. Pada endpoint pagination seperti /case/search, applyMaskingToGroupedPagination membongkar property bernama alerts di dalam tiap baris (apabila tipenya List) dan mem-mask setiap item list secara terpisah lewat iterasi map manual. Behavior ini spesifik ada di MaskService agar nested object array tidak terlewat dari screening field aktif.


Matriks Skenario Operasional

Skenario Masking payload Search filter — current Search filter — target (Flow 4) State lain Next step
Request tanpa X-Data-Access-Key Masking berjalan aktif Filter tetap di-decrypt (tanpa validasi key) Ditolak/di-skip Data disamarkan UI menampilkan cipher.
Token valid tapi cache miss Masking di-skip (plaintext) Filter di-decrypt / lewat Sama dengan current Cache terisi timestamp baru UI menampilkan plaintext.
Token valid dan cache hit Masking di-skip (plaintext) Filter di-decrypt / lewat Sama dengan current DB tidak disentuh untuk user key UI menampilkan plaintext.
Token kadaluwarsa/diubah via cron Masking berjalan (key ditolak) Filter tetap di-decrypt (tanpa validasi key) Ditolak/di-skip Data disamarkan Client harus request key baru (SSE/GET).
Client mengirim plaintext di filter Masking bergantung token Fallback raw value Belum ditentukan di Flow 4 Query DB memakai plaintext tersebut Lolos logika pencarian normal (current).
Enkripsi/dekripsi AES gagal (misal invalid key) Response diset [PROTECTED] Error decrypt ditelan, nilai asli dipakai Ditolak (hard-reject) Nilai riil dihapus dari payload response UI menampilkan [PROTECTED].
Field Integer dikonfigurasi sensitif Response menjadi String [PROTECTED] N/A N/A Type JSON berubah Kemungkinan error di parser frontend.

State Ownership Current

State Authoritative current Projection/consumer
Dynamic fields target SensitiveFieldConfigRepository (T_SENSITIVE_FIELD_CONFIG) Seluruh controller yang memanggil MaskService.
Access key user T_USER.key DataAccessKeyService dan token evaluator.
Temporary valid key Memory: validationCache (Map) Mempercepat pengecekan sebelum DB hit.
Cryptographic secret key KeyService (alias aes-key-01) EncryptionService.

Known Current Gaps yang Relevan untuk Rework

Gap current Dampak
Validasi static field sangat tight ke ExistingCurrTrans Tidak bisa mem-mask field khusus dari entity Alert/Case yang tidak di-share dengan Transaksi (kecuali diselipkan di addtData).
Data Access Key cache per user tidak terhapus saat session diterminasi (hanya clear saat cron) TTL 5 menit menyebabkan zombie validity window bagi token yang direvoke.
Literal string "[PROTECTED]" pada field non-String Mutasi skema JSON mendadak dan berisiko merusak type-safety client frontend.
Masking iteratif memakai ObjectMapper.convertValue dari Object ke Map pada pagination besar Potensi overhead serialization/deserialization ganda pada /search yang me-list ratusan object.
unmaskSearchFilters tidak memvalidasi key dan menelan error decrypt Filter dapat di-decrypt tanpa key valid, dan cipher rusak dieksekusi sebagai teks biasa (diatasi Flow 4).

Endpoint Surface Utama

Surface Endpoint family Capability
Sensitive fields management /sensitive-fields Menambahkan (/add), menghapus (/delete/{fieldName}), melihat field aktif (/), dan melihat field ExistingCurrTrans yang available diregistrasi (/available).
Consumer (Transaction) /existing-transaction/* Memakai header X-Data-Access-Key untuk unmask filter search dan mask response.
Consumer (Alert Acquirer) /alert-acquirer/* Memakai header mask di endpoint search dan findById.
Consumer (Alert Issuer) /alert-issuer/* Memakai header mask di endpoint search dan findById.
Consumer (Case) /case/* Memakai header mask di endpoint search.
User key GET /user/find/{username} Mengembalikan Data Access Key plaintext pada atribut key.
Key rotation stream GET /api/security/access-key/stream SSE event ACCESS_KEY_ROTATED dengan newKey; parameter userId opsional.

Source Trace

Flow/Area Primary source
Orchestration dan nested addtData SecurityConfiguration/Security/Masking/TransactionMaskService.java
Token generator dan rotation cron SecurityConfiguration/Security/Key/DataAccessKeyService.java
Field restriction logic SecurityConfiguration/Security/Masking/SensitiveFieldConfigService.java
Fallback dan encryption logic SecurityConfiguration/Security/Encryption/EncryptionService.java
Sensitive field controller SecurityConfiguration/Security/Masking/SensitiveFieldConfigController.java
User key fetching UserManagement/User/UserController.java (endpoint /user/find/{username})
Controller injection dan unmask invoke AlertManagement/Case/CaseServiceImpl.java; GeneralComponent/Logic/ExistingCurrTrans/ExistingCurrTransController.java; dll.

Catatan Verifikasi Lanjutan