Integration API — Keamanan & Hardening
Auth & Authorization
- 3-tier JWT validation:
requireAuthmembedakan issuer JWT — staff (kesles-merchant-auth) vs merchant (kesles-integration) — payload claim divalidasi sebelum inject ke context. - Role cache 30 detik: Role staff di-cache per
userIDselama 30 detik untuk mengurangi call keauth_serviceper request tanpa mengorbankan freshness. - Merchant JWT hanya berlaku untuk scope sendiri: Handler sandbox yang menerima merchant JWT membaca
ac.MerchantIDdari token — merchant tidak dapat meng-query transaksi milik merchant lain. - SuperAdmin dapat meng-override scope via
?merchant_id=: Hanya staff dengan rolesuperAdminyang dapat menambahkan query parammerchant_iduntuk mengakses data merchant lain.
PSP Secret Handling
- HMAC secret di-encrypt AES-256-GCM:
decryptSecretAESdecode format hexnonce || ciphertext || GCM-tag— secret tidak pernah disimpan plaintext di DB payment_service. - Key lookup via payment_service: Secret selalu di-fetch dari
payment_service /internal/psp/api-keys(bukan DB langsung) — integration_api tidak punya koneksi kedb_payment.
Webhook Sandbox
- URL allowlist untuk PSP tester: Path PSP yang boleh di-forward dibatasi oleh allowlist eksplisit (
/api/psp/v1/merchants,/api/psp/v1/payment-events/,/internal/transactions/,/internal/notifications/) — arbitrary path injection tidak dimungkinkan. - URL scheme validation untuk webhook trigger:
webhook_urlharus dimulai denganhttp://atauhttps://— karakter non-URL atau protokol arbitrer ditolak sebelum request keluar. - Timeout 15 detik pada webhook delivery:
sandboxWebhookClientdikonfigurasi 15s timeout — integration_api tidak dapat di-block oleh endpoint merchant yang lambat/tidak responsif. - Response body dibatasi 8 KB:
io.LimitReader(resp.Body, 8*1024)pada webhook delivery — response sangat besar dari endpoint merchant tidak di-buffer seluruhnya ke memory. - HMAC sandbox signature opsional:
X-Sandbox-Signature: sha256=<hmac>hanya dikirim jika operator mengisisecret— tidak ada secret default/hardcoded.
Sandbox Access Control
- Whitelist eksplisit per merchant: Merchant hanya bisa akses sandbox setelah super admin menambahkan ke
integration.merchant_sandbox_access— tidak ada akses default. - Scoping transaksi pada query:
getSandboxTransactionmenyertakanmerchant_id = $2pada WHERE untuk merchant JWT — injection transaction_id milik merchant lain di-reject di query layer. - Idempotent enable:
enableMerchantSandboxmenggunakanON CONFLICT (merchant_id) DO NOTHING— double-enable tidak membuat duplikat record.
Hardening Baseline (8-item)
| Item | Status |
|---|---|
validateConfig fail-fast (fatal jika JWT_SECRET/token kosong di production) | ✅ |
| Constant-time API key comparison | ✅ |
| HTTP client timeout (10s default, 15s webhook, 20s PSP) | ✅ |
Graceful shutdown (SIGTERM → http.Server.Shutdown) | ✅ |
slog event taxonomy (snake_case, tidak ada PII di log) | ✅ |
| PostgreSQL pool: 25 max, 5 idle, 5min lifetime | ✅ |
pgx/v5 via stdlib adapter | ✅ |
APP_ENV=production eksplisit di .env line 1 | ✅ |