Dashboard API — Keamanan & Hardening
Semua item di bawah telah diimplementasikan dan diverifikasi per audit keamanan 2026-05-29–2026-06-05.
Keamanan & Hardening
Auth & Authorization
- Image proxy hanya mengizinkan CDN domain yang disetujui:
allowedProxyHostsdikonfigurasi dengan daftar putih CDN domain resmi — semua IP internal dan alamat jaringan pribadi dihapus dari daftar yang diizinkan. Endpoint proxy juga dilindungi olehwithDashboardAuthsehingga tidak bisa diakses tanpa sesi yang valid. - IP allowlist PSP tidak bisa dibypass via header klien:
pspClientIP()membaca IP hanya darir.RemoteAddratauX-Real-IPyang di-set oleh nginx — headerX-Forwarded-Foryang berasal dari klien diabaikan sehingga serangan bypass allowlist via header palsu tidak mungkin dilakukan. - PSP secret tanpa enkripsi ditolak keras saat startup:
decryptSecret()menolak semua nilai yang tidak diawali prefixenc:v1:. Selain itu, probe scan dijalankan saatNewServerdipanggil — jika ada PSP key yang tersimpan dalam plaintext, server langsung panic/log.Fataldan tidak mau berjalan. Key yang valid secara kriptografis adalah syarat mutlak untuk startup. - Master encryption key divalidasi sekali saat startup, bukan per-request:
loadMasterKey()dipanggil diNewServerdan akanpanic/log.Fataljika key tidak valid atau tidak ada — tidak ada window di mana server berjalan dengan key yang belum tervalidasi.
Role-Based Access Control
- Dev tools dinonaktifkan total di production:
ENABLE_DEV_TOOLS=falsedi.env.productionmemastikan semua 7 endpoint/dev/*tidak didaftarkan ke router sama sekali saat production build. Selain itu, route tersebut hanya diregistrasikan ketikaAPP_ENV != productionatau flag eksplisitENABLE_DEV_TOOLS=truediset. - Bulk email dev endpoint dibatasi ketat ke superadmin:
handleDashboardDevEmailBulkExecutemenggunakan pemeriksaanhasAnyRole('superadmin')secara eksplisit — meskipunreportsPlanningRolesdi masa depan diperluas, role lain tidak akan mendapatkan akses ke endpoint ini karena guard hard-coded ke superadmin. - Privilege escalation vertikal dari admin ke superadmin dicegah:
assignUserRoleHandlermemiliki guard eksplisit yang memeriksa bahwa hanya superadmin yang dapat mengassign role superadmin ke pengguna lain — admin tidak bisa mempromosikan dirinya sendiri atau orang lain ke level tertinggi. - Data KYC dan PII merchant hanya bisa diakses role berwenang:
handleMerchantRegistrationDetaildangetMerchantKYCPhotosHandlerdibatasi kemerchantWriteRoles(superadmin, admin, operations) — role dengan akses terbatas seperti viewer atau partner_admin tidak bisa melihat foto KYC, NPWP, atau data identitas sensitif merchant. - Seluruh mutation KYC dilindungi role gate: Semua 8 mutation handler untuk approve/reject/update KYC merchant memiliki pemeriksaan
merchantWriteRolesatau kombinasi superadmin+admin+operations — tindakan yang berdampak pada status kepatuhan merchant tidak bisa dieksekusi oleh semua authenticated dashboard user. - Enumerasi nomor HP staf merchant dibatasi:
listMerchantStaffHandlerdibatasi ke superadmin, admin, operations, dan support — role viewer atau partner_admin tidak bisa melakukan enumerasi data kontak staf merchant. - Daftar merchant dengan data finansial sensitif terlindungi:
listMerchantsHandlerdibatasi kemerchantWriteRoles— daftar merchant beserta data finansial (MDR, NMID, MID, TID, PSP ID) tidak terekspos ke semua session dashboard yang terautentikasi. - Detail merchant penuh hanya untuk role berwenang:
getMerchantHandlermenggunakan gate profil +merchantWriteRoles— detail NMID, MID, TID, PSP ID, NPWP, MDR tidak bisa dibaca oleh semua role dashboard. - Inventori device hanya bisa dilihat role berwenang:
handleDashboardDevices,listDeviceModelsHandler, dangetDeviceModelHandlersemuanya dibatasi kedeviceWriteRolesataudeviceModelWriteRoles— daftar device inventory dan sinyal harga model device tidak terekspos ke semua session dashboard. - Serial number dan SIM card terminal terlindungi:
listTerminalStockHandlerdanhandleDashboardTerminalStockUnitsdibatasi kedeviceWriteRoles— data sensitif hardware seperti serial number dan nomor SIM card tidak bisa diakses sembarangan. - Enumerasi terminal payment merchant dibatasi:
listMerchantDevicesHandlermenggunakan gate profil +deviceWriteRoles— semua session dashboard tidak bisa melakukan enumerasi terminal payment milik merchant. - Struktur pricing dan commission rate partner terlindungi:
listCommercialPlansdanlistRevenueShareRulespada cabang GET dibatasi kepartnerWriteRoles— detail pricing tier, split percentage, dan commission rate tidak terekspos ke semua role. - Credentials partner hanya bisa dienumerasi role berwenang:
handleDashboardPartnerCredentialsListpada GET memilikihasAnyRole(partnerWriteRoles)— viewer dan support tidak bisa melihat daftar credentials partner.
SSRF & Path Traversal Prevention
- Path traversal pada tester path dicegah: Fungsi allowlist tester (mis.
notifTesterPathAlloweddanpartnerTesterPathAlloweddi dashboard_api) hanya mengizinkan path yang exact-match atau prefix-match daftar yang disetujui, sehingga manipulasi path ke endpoint internal di luar yang diizinkan tidak berhasil. Guard PSP testerpspTesterPathAllowed(yang menolak path mengandung..sebelum prefix matching) kini berada di integration_api setelah PSP tester di-cutover dari dashboard_api. - SSRF dari partner webhook dicegah dengan validasi host resolusi DNS:
validateWebhookURLmelakukannet.LookupHostuntuk semua IP yang ter-resolve dari hostname yang diberikan, kemudian menolak loopback (127.x), alamat private (RFC 1918), dan link-local — URL webhook yang dikirim partner tidak bisa diarahkan ke layanan internal.
Rate Limiting
- Endpoint payment event dilindungi rate limiter berbasis DB:
pspRateLimiterdandefaultPSPRateLimitermenegakkan batas request yang dikonfigurasi perkey_iddari database — endpoint notifikasi pembayaran tidak bisa di-flood tanpa batas oleh PSP atau pihak yang menyalahgunakan key.
Data Integrity & Audit Trail
- Audit trail immutable dengan actor context:
insertAuditEventmenggunakanset_config LOCALuntuk menyetelapp.actor_user_iddanapp.actor_roledi level transaksi database. Triggerfn_audit_events_immutablemencegahUPDATEdanDELETEpada tabel audit. Fungsifn_status_change_audit_fallbackbertindak sebagai safety-net untuk perubahan status yang tidak melalui path normal. Dataactor_emailhanya diisi untuk role superadmin, admin, dan owner. - Kegagalan commit audit trail tercatat via slog: Blok audit best-effort di
dashboard_shipping_orders.go(4 state transition:packed,dispatched,delivered,returned) menggunakanif commitErr := tx.Commit(); commitErr != nil { slog.Warn(...) }— kegagalan commit tidak lagi ditelan diam-diam (_ = tx.Commit()), sehingga ada observability saat audit trail gagal disimpan. Diimplementasikan 2026-06-13. - Body request di endpoint proxy dibatasi ukurannya:
forwardCoreAPImenggunakanhttp.MaxBytesReaderdengan batas 2 MB sebelumio.ReadAll— serangan DoS amplification melalui pengiriman body besar ke dashboard_api dicegah di lapisan middleware. - Global body size cap 1 MB untuk semua JSON endpoint: Middleware
withBodyLimitdiserver.gomembatasi semua POST/PUT/PATCH request ke 1 MB. Requestmultipart/form-datadikecualikan agar upload handler tetap bisa set limit sendiri (5 MB untuk foto/logo/KYC). Menutup M-176 dan 36 handler JSON lain yang sebelumnya unprotected. Diimplementasikan 2026-06-11.
Secret & Configuration Management
- MinIO credentials tidak memiliki fallback hardcoded:
MINIO_ACCESS_KEYdanMINIO_SECRET_KEYdiconfig.gotidak memiliki nilai default fallback — jika kosong di environment non-development, startup langsung gagal (fail-fast assertion) sehingga tidak ada credential dummy yang secara tidak sengaja digunakan di production. - CLI enkripsi PSP membaca secret dari stdin atau file: Tool
psp-encryptmenerima plaintext secret via stdin atau path file, bukan via argumen command line — secret tidak pernah muncul di outputps auxatau log shell history yang bisa dilihat proses lain di sistem. - Kode acak kriptografis selalu diverifikasi:
randomSixteenDigitsdanrandomEightDigitsmengembalikan error jikacrypto/randgagal — caller tidak pernah menerima kode all-zero yang mudah ditebak sebagai fallback diam-diam.
Email Security
- Header injection via CRLF pada email massal dicegah:
buildBulkEmailMessagemenggunakansanitizeHeaderyang menghapus karakter\rdan\ndari fieldFrom,To, danSubject— injeksi header email melalui manipulasi CRLF pada input user tidak mungkin dilakukan.