Keamanan dan Isolasi Workspace
Mekanisme keamanan yang menjaga batas antar-workspace (tenant) terdiri atas: autentikasi JWT, sesi perangkat, kebijakan kata sandi, pembatasan laju, batas peran dan izin, isolasi baris data (RLS), serta verifikasi tanda tangan webhook. Dalam dokumentasi produk, workspace adalah istilah utama; pada database dan API batas yang sama disebut account/account_id.
Tujuan
- Memahami alur autentikasi: token JWT, sesi perangkat, dan kata sandi.
- Mengetahui batas yang ditegakkan server: peran/izin, isolasi per-akun, dan proteksi brute-force.
- Memverifikasi tanda tangan webhook masuk dan keluar dengan benar.
- Mengetahui keterbatasan keamanan saat ini (misalnya tidak ada MFA) sebelum menaruh data sensitif.
Untuk siapa
- Owner/admin yang mengelola pengguna, sesi, dan kebijakan keamanan akun.
- Developer yang memanggil API atau membangun integrasi webhook.
Prasyarat
- Aplikasi berjalan dengan
JWT_SECRET(minimal 32 byte),JWT_ISSUER, danJWT_AUDIENCEterkonfigurasi; aplikasi menolak mulai bila kunci JWT kosong. - Pengguna memiliki peran aktif (
users.status='active'danmemberships.status='active'). - Untuk widget dari situs eksternal: daftarkan origin pada
CORS_ORIGINS(lihat Deployment dan runtime).
Langkah-langkah
1. Masuk dan memahami token JWT
- Masuk dengan email dan kata sandi pada halaman masuk.
- Server menerbitkan JWT dengan algoritma HS256 (algoritma lain ditolak), berisi klaim
account_id,user_id,role, danpermissions, denganissuer,audience, dan masa berlaku (8 jam) yang divalidasi pada setiap permintaan. - Kirim token pada header
Authorization: Bearer <token>untuk seluruh permintaan API terautentikasi. - Token tidak dapat diperbarui (refresh) — setelah kedaluwarsa, masuk kembali.
2. Mengelola sesi perangkat
- Buka Profil → Sesi untuk melihat perangkat yang sedang masuk (
GET /profile/sessions). - Cabut sesi yang tidak dikenal dari daftar Anda (
DELETE /profile/sessions/:id). - Owner/admin dapat melihat sesi pengguna lain (
GET /users/:id/sessions); sesi lintas-akun atau pengguna yang tidak dikenal mengembalikan 404 agar tidak membocorkan keberadaan akun. - Server menyimpan hanya hash SHA-256 token dan
jti— token mentah tidak pernah disimpan; mencabut sesi mencabut jti tersebut sehingga token yang beredar langsung ditolak (revoked_at IS NULL AND expires_at > now()).
3. Mengubah kata sandi
- Buka pengaturan profil → ubah kata sandi.
- Masukkan kata sandi saat ini — server memverifikasinya (bcrypt) sebelum mengizinkan perubahan.
- Kata sandi baru harus 8–72 byte; disimpan sebagai hash bcrypt (
DefaultCost), tidak pernah sebagai teks mentah. - Pastikan kata sandi tidak dipakai ulang lintas layanan; tidak ada alur "lupa kata sandi" yang mengirim tautan reset.
4. Memahami proteksi brute-force dan pembatasan laju
- Kegagalan masuk dibatasi 5 percobaan per 5 menit per IP, lalu akun/IP terkunci 15 menit; respons menyertakan header
Retry-After. - Permintaan terautentikasi dibatasi 300/menit per akun (atau per IP); jalur webhook masuk dikecualikan karena sudah diverifikasi HMAC dan dideduplikasi.
- Pembuatan sesi widget memiliki proteksi terpisah (10 percobaan per 5 menit, terkunci 10 menit; lihat Widget Situs Web).
5. Memahami batas peran dan izin
- Peran yang tersedia: owner, admin, agent, dan viewer. Owner/admin memegang izin kelola akun (
manage_account,manage_users,export_data,view_audit_log,manage_webhooks, dan lain-lain); agent bekerja dalam lingkup tim; viewer hanya membaca. - Izin diperiksa di server pada setiap permintaan (middleware
RequireRole/RequirePermission) dan gagal dengan 403FORBIDDEN— bukan sekadar disembunyikan di antarmuka. - Klaim peran/izin dibawa dalam JWT dan dimuat saat masuk dari tabel
role_permissions/permissions; perubahan peran berlaku pada masuk berikutnya.
6. Memahami isolasi workspace (RLS)
- Setiap tabel berisi data akun diberlakukan Row-Level Security (ENABLE + FORCE RLS) dengan kebijakan
USING (account_id = current_account_id()). - Pada setiap transaksi, server menjalankan
SET LOCAL app.account_iddanapp.user_iddari klaim token yang sudah divalidasi; fungsicurrent_account_id()/current_user_id()membacanya, sehingga kueri apa pun hanya menjangkau baris akun yang sedang aktif. - Jalur yang tidak diautentikasi (misalnya webhook masuk) memakai konteks penyewa eksplisit (
dbconn.WithTenantContext) — bukan "tanpa akun". - Untuk menguji isolasi antar-penyewa secara langsung, ikuti langkah verifikasi di data-governance.md (endpoint
/data-governance/verify-isolation).
7. Memahami isolasi WAHA multi-session
- Satu workspace menyimpan satu set Base URL, API key, dan webhook secret WAHA yang terenkripsi pada konfigurasi milik workspace.
- Satu konfigurasi tersebut dapat mengakses banyak WAHA session. Setiap session diikat ke satu inbox WhatsApp dalam workspace yang sama.
- Resolver webhook mencari session yang tepat sebelum menetapkan
account_iddaninbox_id; setelah itu seluruh penulisan dilakukan dalam transaksi RLS workspace tersebut. - Banyak user dalam workspace boleh memakai session yang sama jika memiliki akses ke inbox terkait. Ini adalah sharing di dalam workspace, bukan sharing credential lintas tenant.
- Session WAHA yang sama tidak boleh didaftarkan ke dua workspace. Custelio belum mendukung shared channel lintas-workspace; gunakan session dan credential yang berbeda untuk menjaga routing serta audit tetap deterministik.
8. Memverifikasi tanda tangan webhook
Webhook masuk (dari penyedia, misalnya WAHA):
- Penyedia menandatangani body mentah dengan HMAC (SHA-256 default, SHA-512 tersedia) menggunakan rahasia per-akun.
- Header yang dikirim:
X-Webhook-Hmac(nilai hex) danX-Webhook-Hmac-Algorithm. - Server membandingkan tanda tangan dengan perbandingan waktu-konstan; rahasia kosong atau akun kosong → 401. Event dideduplikasi berdasarkan ID event; tipe event yang tidak dikenal dijawab 200 "ignored" tanpa percobaan ulang.
- Untuk integrasi Anda sendiri yang menerima panggilan balik, gunakan pola yang sama: verifikasi HMAC atas body mentah, jangan pernah memercayai body tanpa tanda tangan. Lihat Webhook masuk dan keluar.
Webhook keluar (langganan acara domain):
- Buat langganan dengan endpoint dan rahasia penanda tangan; rahasia disimpan terenkripsi dan tidak pernah dikembalikan lewat jalur baca HTTP mana pun.
- Pengiriman menyertakan header
X-Webhook-Signatureberbentuksha256=<hex HMAC-SHA256 dari body>. - Status pengiriman: 2xx dianggap sukses; kegagalan masuk antrean mati (DLQ) setelah anggaran percobaan ulang habis; pengiriman dapat diputar ulang dari daftar pengiriman.
- Verifikasi header ini di penerima Anda sebelum memproses payload.
9. Menjaga privasi dan jejak audit
- Jangan mencatat (log) token mentah, kata sandi, rahasia penanda tangan, atau PII di aplikasi klien atau skrip Anda sendiri — server tidak menyimpannya dalam bentuk tersebut.
- Gunakan token platform (prefiks
crma_) hanya pada integrasi server-side dengan scope minimum; jangan pernah mengirim token admin ke browser. - Endpoint publik widget (bootstrap sesi, konfigurasi) sengaja tidak diautentikasi; lindungi halaman sematan dan batasi paparan data publik (lihat Widget Situs Web).
Tanda berhasil
- Login gagal berulang dari IP yang sama ditolak dengan 429 dan
Retry-After; login sah tetap berhasil setelah jendela berakhir. - Sesi yang dicabut langsung kehilangan akses; daftar sesi tidak pernah menampilkan token atau hash.
- Pengguna dengan peran viewer tidak dapat melakukan perubahan; permintaan tanpa izin menerima 403.
- Kueri lintas-akun (misalnya mencoba membaca baris akun lain lewat API) tidak mengembalikan data apa pun.
- Penerima webhook dapat memverifikasi
X-Webhook-Signature/X-Webhook-Hmacdan menolak payload tanpa tanda tangan yang sah.
Jika terjadi masalah
- 401 token tidak valid/kedaluwarsa/dicabut: token salah, lewat 8 jam, atau sesinya dicabut. Masuk kembali untuk memperoleh token baru.
- 403
FORBIDDEN: peran tidak memiliki izin yang diminta; minta owner/admin menyesuaikan peran, atau gunakan akun dengan izin sesuai. - 429 dengan
Retry-After: terkena pembatasan laju atau kunci brute-force; tunggu hingga jendela berakhir sebelum mencoba lagi. - 422 aturan kata sandi: kata sandi baru di luar 8–72 byte, atau kata sandi saat ini salah; perbaiki lalu ulangi.
Batasan
- Tidak ada MFA/TOTP/2FA — autentikasi hanya email+kata sandi dengan sesi JWT; pertimbangkan lapisan tambahan untuk akun berisiko tinggi.
- Tidak ada alur "lupa kata sandi"/reset berbasis tautan; pemulihan akun bergantung pada admin.
- Penyimpanan brute-force bersifat in-memory per instance: pencacah hilang saat proses dimulai ulang dan tidak dibagi antar-instance; pada penempatan multi-instance, gunakan pembatas di lapisan lain bila perlu.
- Token tidak dapat diperbarui (refresh); kedaluwarsa 8 jam mengharuskan masuk ulang.
- Penandatanganan webhook keluar (
X-Webhook-Signature) dan detail pengiriman/DLQ tersedia pada implementasi saat ini; dokumentasi pengembang menandai cakupan tertentulimited— lihat Webhook masuk dan keluar untuk statusnya. - CORS adalah kontrol peramban, bukan otorisasi API; klien non-peramban tidak terikat daftar origin.
- Perubahan peran/izin baru berlaku pada masuk berikutnya (klaim di dalam JWT).
- Detail penetapan izin per peran dapat berubah; lihat sumber kebenaran di
apps/api/internal/securityuntuk daftar terkini.
Tugas terkait
- Tata Kelola Data — retensi, ekspor, penghapusan, dan cara memverifikasi isolasi antar-penyewa.
- Masuk dan ruang kerja — alur login pengguna.
- Webhook masuk dan keluar — format tanda tangan dan status dukungan.
- Widget Situs Web — endpoint publik widget dan batas CORS.
- Portal Bantuan dan Survei CSAT — konten publik portal.
- Referensi API — endpoint autentikasi, sesi, dan webhook.
- Storage dan platform — penyimpanan kredensial dan praktik token platform.
- Deployment dan runtime — konfigurasi environment dan CORS.