Lewati ke konten utama

Firebase Service — Keamanan & Hardening

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


Keamanan & Hardening

Autentikasi & Validasi Token

  • JWT wajib klaim exp: Parser Firebase JWT dikonstruksi dengan WithExpirationRequired() sehingga token yang tidak memiliki klaim exp — misalnya yang dibuat via tooling debug atau JWKS override di staging — ditolak secara tegas dan tidak pernah diterima sebagai valid tanpa batas waktu.

  • Masking FCM token di error response: Helper maskToken dipakai di seluruh error response yang menyertakan push_token. Token FCM asli tidak pernah muncul di caller response maupun log storage — hanya versi tersamar yang tercatat.

  • Masking penuh di endpoint list/lookup: Fungsi toTokenResponse() menggunakan format maskTokenFull() (pola xxxx****xxxx) sehingga full FCM registration token tidak bisa diekstrak dari endpoint list maupun lookup meskipun caller memiliki API key internal yang valid.

  • JWKS dibaca dengan batas ukuran: Response dari endpoint Google JWKS dibaca via io.LimitReader dengan batas 512 KB. Server JWKS yang misbehaving — termasuk override di lingkungan non-production — tidak bisa menguras memori proses firebase_service.


Input Validation & Pembatasan Body

  • Middleware body limit global: maxBodySizeMiddleware menggunakan http.MaxBytesReader 1 MB yang dibungkus di level mux, bukan per-handler. Seluruh POST endpoint (send, bulk send, broadcast, token register, prefs, auth) terlindungi seragam dari serangan body sangat besar tanpa perlu konfigurasi per-route.

  • Batas token per request bulk send: handleSendBulk membatasi maksimum 500 token per request. Caller tidak bisa mengeksploitasi endpoint ini untuk menguras connection pool database atau FCM quota sekaligus via satu request besar.

  • Batas pagination token list: handleTokenListActive meng-enforce limit maksimum 5000 terlepas dari nilai ?limit yang dikirim caller. Query ?limit=1000000 di-cap otomatis.

  • Validasi format Operator User ID: handleCancelBroadcast memvalidasi format UUID untuk header X-Operator-User-ID sebelum mencatat audit log. Audit trail tidak bisa diatribusikan ke user ID sembarang atau string tak berstruktur.


Broadcast Security & Idempotency

  • Transaksi DB dilepas sebelum fan-out FCM: FetchAndLockReadyBroadcast melakukan commit segera setelah status broadcast berubah ke sending. Fan-out pengiriman FCM ke semua token berjalan di luar transaksi database, sehingga broadcast berskala besar tidak bisa mensaturasi connection pool.

  • Override rate-limit wajib identitas operator: Field override_reason hanya diproses jika header X-Operator-User-ID yang valid turut disertakan. Bypass rate limit tanpa identitas operator yang jelas tidak dimungkinkan; setiap penggunaan dicatat via slog.Warn sebagai audit trail.

  • Idempotency SETNX atomik: Idempotency middleware menggunakan operasi SETNX atomik Redis sebelum handler dieksekusi. Dua request concurrent dengan idempotency key yang sama tidak bisa keduanya meng-trigger pengiriman FCM — hanya satu yang dieksekusi, yang lain mendapat respons dari cache.

  • Fail-open idempotency dengan notifikasi eksplisit: Saat Redis tidak tersedia, middleware mengizinkan request berlanjut (fail-open) dan menambahkan header X-Idempotency-Skipped: redis-error pada respons disertai slog.Warn. Operator mengetahui kapan protection tidak aktif tanpa ada request yang diblokir secara diam-diam.


Information Disclosure Prevention

  • Error Google API tidak bocor ke caller: Saat FCM send failure atau Google OAuth failure, detail error dari Google API (termasuk Firebase project ID, service account info, dan quota information) hanya dicatat via slog.Error di sisi server. Caller hanya menerima pesan generik seperti fcm send failed (status 429) atau google oauth failed (status 503).

  • Endpoint /ready tanpa detail PostgreSQL: Saat koneksi database gagal, /ready mengembalikan string ping_failed tanpa menyertakan hostname DSN, port, atau username. Infrastruktur monitoring tetap mendapat sinyal kesehatan tanpa mengekspos topologi database.

  • Endpoint /health minimal: /health hanya mengembalikan {service, status}. Field firebase_configured, postgres_configured, dan app_env telah dihapus dari response publik. Attacker tidak bisa memetakan state konfigurasi internal (apakah Firebase credentials dimuat, environment staging vs production) via endpoint unauthenticated ini.