engine-whatsapp tidak memiliki autentikasi. Semua endpoint /api/* dapat diakses publik. Keamanan wajib ditangani di perimeter jaringan — service ini diasumsikan hanya terjangkau dari backend Dazo dan jaringan internal.
Tidak ada autentikasi
Tidak ada middleware auth di src/routes/itemRoutes.ts atau src/app.ts. Bandingkan dengan engine-bot yang punya verifyToken middleware.
Implikasi
| Risiko | Dampak |
|---|---|
Buat sesi baru (POST /api/new) | Attacker dapat membuat sesi WhatsApp dan menerima QR — tapi butuh scan QR di HP mereka |
Kirim pesan (POST /api/send-message) | Attacker dapat kirim pesan atas nama device yang aktif |
Logout (GET /api/logout) | Attacker dapat memutus sesi device — DoS |
Push notif (POST /api/push-notif) | Attacker dapat injeksi notifikasi palsu ke frontend |
Submit pixel (POST /api/submit-pixel) | Attacker dapat injeksi pixel event ke Facebook dengan access_token mereka sendiri |
Mitigasi — perimeter jaringan
Service ini harus di-deploy di belakang:
- Reverse proxy dengan IP allowlist (hanya IP
backend-go,dazoapp, frontend) - Firewall di level server (port
5002hanya terbuka untuk internal) - VPN / private network antara service
- Cloudflare Access atau equivalent untuk akses eksternal
Socket.IO — CORS terbuka
// src/service/socketService.ts
this.io = new SocketIOServer(httpServer, {
cors: {
origin: "*", // ← terbuka untuk semua origin
methods: ["GET", "POST"]
}
});CORS * mengizinkan client dari origin apapun connect ke Socket.IO. Frontend Dazo adalah konsumen utama, tapi attacker dapat connect juga.
Mitigasi Socket.IO
- Batasi CORS ke domain frontend Dazo yang valid
- Tambahkan auth middleware di socket handshake (
io.use(...)) — verifikasi JWT atau token khusus - Pertimbangkan namespace terpisah untuk admin vs frontend
Lihat Tech Debt.
Enkripsi AES-256-CBC — hard-coded
src/utils/helper.ts memakai enkripsi AES-256-CBC untuk field id di event getmessage dan message_status_update:
// src/utils/helper.ts:12-14
const encryptionMethod = 'AES-256-CBC';
const secret = 'My32charPasswordAndInitVectorStr'; // ← hard-coded
const iv: string = secret.substr(0, 16); // ← IV diturunkan dari secretField id yang dienkripsi: encrypt(instanceId + userNumber, ...). Tujuannya menyembunyikan instanceId + nomor dari frontend — bukan perlindungan kuat. Tapi dengan secret dan IV hard-coded, siapapun yang membaca source dapat membaliknya.
Mitigasi
- Pindahkan secret ke env var (
ENCRYPTION_SECRETdi.env) - Generate IV acak per enkripsi (16 byte random)
- Pertimbangkan HMAC untuk verifikasi integritas, bukan enkripsi bidirectional
Media URL — tidak ada SSRF protection
POST /api/send-media menerima url field dan mengirim ke WhatsApp. Sebelum kirim, ada validasi isMediaUrlValid:
// src/utils/helper.ts:294-300
export async function isMediaUrlValid(url: string): Promise<boolean> {
try {
const response = await fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(5000) });
return response.ok; // 200-299
} catch {
return false;
}
}Hanya cek HTTP HEAD — tidak ada:
- Protocol allowlist (bisa
file://,ftp://) - Domain/IP allowlist (bisa
http://169.254.169.254/— AWS metadata) - Private network block (
10.x,192.168.x,127.0.0.1) - Redirect restriction
- Size limit
- Content-type validation
Webhook — tidak ada signature
Engine mengirim webhook ke backend Dazo tanpa signature atau credential:
// src/utils/helper.ts:33
const res = await axios.post(targetUrlBackend, payload);Tidak ada HMAC signature, tidak ada Bearer token, tidak ada shared secret. Backend Dazo tidak dapat memverifikasi bahwa request benar-benar dari engine-whatsapp.
Mitigasi
- Tambahkan HMAC signature di header (
X-Webhook-Signature) - Atau batasi IP source di backend Dazo (hanya IP server engine-whatsapp)
WEBHOOK_URL mode dummy
Jika WEBHOOK_URL mengandung webhook-test, webhook dilewati. Ini bukan masalah keamanan, tapi perlu diingat: jangan pakai webhook-test di produksi — backend tidak akan menerima pesan masuk sama sekali.
Ringkasan risk assessment
| Area | Risiko | Status |
|---|---|---|
| Tidak ada auth REST API | Kritis | 🔴 Tidak ada mitigasi di kode — perimeter wajib |
Socket.IO CORS * + no auth | Tinggi | 🔴 Frontend dapat di-spoof |
| Secret AES hard-coded | Tinggi | 🔴 Source dapat diakses |
| IV statis | Tinggi | 🔴 Enkripsi lemah |
| SSRF media URL | Sedang | 🟡 Hanya cek HEAD 200 |
| Webhook tanpa signature | Sedang | 🟡 Backend tidak verifikasi source |
| Log PII | Sedang | 🟡 Log dapat memuat nomor, nama, URL media |
Yang TIDAK boleh diubah tanpa koordinasi
| Area | Lokasi |
|---|---|
| Bentuk payload webhook | src/utils/helper.ts — sendWebhook |
| Event Socket.IO shape | src/service/socketService.ts, src/service/notificationService.ts |
Field instanceId enkripsi | src/utils/helper.ts — encrypt() |
Langkah berikutnya
- Known issues? Baca Tech Debt.
- Detail webhook payload? Baca Webhook Reference.
- Cara kerja sesi? Baca Session Lifecycle.