Skip to main content

Keamanan Partner Service

Dokumen ini mencatat keunggulan keamanan yang telah diimplementasikan pada partner_service (port 8086).


Keamanan & Hardening

Semua item di bawah telah diimplementasikan dan diverifikasi per audit keamanan 2026-05-29–2026-06-05.

Input Validation & Body Limits

  • Global body limit via middleware: withBodyLimit middleware 1 MB dipasang membungkus seluruh mux di server.go, sehingga semua 20+ POST handler (termasuk /api/partner/v1/auth/token dan seluruh endpoint admin) terlindungi dari request body berlebihan tanpa perlu MaxBytesReader individual di setiap handler. Ini mencegah serangan DoS berbasis memory exhaustion dari credential holder maupun pihak tidak dikenal.

  • Validasi UUID sebelum query database: handler handleInternalCredentialByKey/handleInternalPartnerLookupNames memvalidasi format UUID setiap elemen query parameter (isValidUUID) sebelum diteruskan ke store LookupPartnerNamesByIDs, yang mencocokkan via id::text = ANY(string_to_array($1, ',')). String non-UUID tidak pernah mencapai layer database, mencegah potensi injeksi via invalid cast.


Authentication & Authorization

  • Fail-fast pada konfigurasi JWT: MakeAuthMiddleware melakukan log.Fatal saat JWT_SECRET kosong di semua environment non-local (staging, production, dll). Tidak ada fallback bypass untuk environment selain local — sistem tidak mau berjalan dengan konfigurasi auth yang tidak lengkap.

  • Ownership bypass via nil claims ditutup: handlePartnerScoped mengembalikan 401 saat JWT claims bernilai nil, sehingga mode dev tanpa JWT tidak bisa melewati pengecekan kepemilikan partner. Kondisi if claims == nil || claims.PartnerID != partnerID menjamin nil claims selalu ditolak.

  • Auth middleware aktif di semua partner API endpoint: Seluruh 5 non-auth routes di bawah /api/partner/v1/ sekarang dibungkus m.auth(handler) di routes.go. Tidak ada endpoint partner API yang dapat diakses tanpa token JWT valid.

  • Global scope fail-closed: Mode global scope yang memungkinkan akses lintas-merchant sekarang mencatat slog.Warn audit di listGlobal dan CanAccessMerchant. Dev mode bypass telah diubah dari return true, nil menjadi return false, nil — sistem memilih tolak daripada izinkan saat terjadi ambiguitas. Empat credential placeholder global telah dinonaktifkan di database.


SSRF Prevention

  • Validasi webhook URL blokir jaringan internal: validateWebhookURL() helper memastikan URL webhook yang dikonfigurasi wajib menggunakan HTTPS dan memblokir semua loopback address serta private IP range via net.ParseIP().IsLoopback() dan .IsPrivate(). Webhook dispatcher tidak akan pernah mengirim request ke jaringan internal meskipun admin menyimpan URL internal ke database.

Credential Security & Audit Trail

  • Scope resolution berbasis credential level: ScopeResolver membaca COALESCE(credential.access_scope_mode, partner.access_scope_mode) sehingga credential yang dikonfigurasi restricted benar-benar dibatasi aksesnya, meskipun partner induknya memiliki scope global. Sebelumnya, scope level credential diabaikan sepenuhnya.

  • Scope credential dipropagasi ke JWT: partnerJWTClaims menyertakan field AccessScopeMode yang di-populate dari credential aktif. Downstream scope resolution di setiap request menghormati scope level credential, bukan hanya scope level partner.

  • Audit log lifecycle credential: Operasi RotateCredential, RevokeCredentialByID, dan CreateHMACCredential mencatat event ke partner.api_audit_logs. Forensik post-incident dapat menentukan kapan dan oleh siapa credential di-rotate atau di-revoke.

  • Audit log akses global scope ke database: CanAccessMerchant dalam mode global menulis ke partner.api_audit_logs (sebelumnya hanya slog.Warn in-memory). Setiap akses lintas-merchant tercatat secara persisten untuk kebutuhan audit dan deteksi anomali.

  • Upgrade transparan SHA-256 ke bcrypt: Credential lama yang tersimpan sebagai SHA-256 secara otomatis di-upgrade ke bcrypt pada saat verifikasi berhasil. Ini mengeliminasi timing oracle antara dua hash type secara bertahap tanpa membutuhkan migrasi massal atau downtime.

  • Endpoint credential sensitif dibatasi ke high-privilege key: GET /internal/credentials/by-key hanya dapat diakses oleh internal key bereputasi tinggi yang terpisah. hmac_secret_encrypted tidak bocor ke semua internal key holder, membatasi radius blast jika sebuah internal key dikompromikan.


Ringkasan Postur Keamanan

AreaStatus
Body limit global (DoS prevention)Aktif
JWT fail-fast semua env non-localAktif
Auth middleware semua partner API routesAktif
Global scope fail-closed + audit logAktif
SSRF prevention via webhook URL validationAktif
Credential-level scope resolutionAktif
Audit log lifecycle credentialAktif
Upgrade hash SHA-256 → bcrypt transparanAktif
Pembatasan endpoint credential sensitifAktif
UUID validation sebelum DB queryAktif