D
Engineering

Security

Engine WhatsApp tidak punya autentikasi — semua endpoint public. Secret enkripsi AES-256-CBC hard-coded dengan IV statis. Socket.IO CORS terbuka. Keamanan wajib di perimeter jaringan.

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

RisikoDampak
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 5002 hanya terbuka untuk internal)
  • VPN / private network antara service
  • Cloudflare Access atau equivalent untuk akses eksternal

Socket.IO — CORS terbuka

ts
// 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:

ts
// 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 secret

Field 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_SECRET di .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:

ts
// 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:

ts
// 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

AreaRisikoStatus
Tidak ada auth REST APIKritis🔴 Tidak ada mitigasi di kode — perimeter wajib
Socket.IO CORS * + no authTinggi🔴 Frontend dapat di-spoof
Secret AES hard-codedTinggi🔴 Source dapat diakses
IV statisTinggi🔴 Enkripsi lemah
SSRF media URLSedang🟡 Hanya cek HEAD 200
Webhook tanpa signatureSedang🟡 Backend tidak verifikasi source
Log PIISedang🟡 Log dapat memuat nomor, nama, URL media

Yang TIDAK boleh diubah tanpa koordinasi

AreaLokasi
Bentuk payload webhooksrc/utils/helper.ts — sendWebhook
Event Socket.IO shapesrc/service/socketService.ts, src/service/notificationService.ts
Field instanceId enkripsisrc/utils/helper.ts — encrypt()

Langkah berikutnya