Sandbox Wasm relevan ketika aplikasi harus menjalankan logika yang berasal dari input tidak tepercaya: memvalidasi file upload, mem-parse format kompleks, atau mengeksekusi template/user script terbatas. Tujuannya bukan membuat input berbahaya menjadi aman secara ajaib, melainkan memperkecil dampak jika parser atau script tersebut bermasalah.

Dalam praktiknya, pendekatan ini berguna untuk hardening backend web: jalankan komponen berisiko di lingkungan WebAssembly (Wasm) yang dibatasi, kontrol CPU/memori/timeout, batasi I/O, dan tetap lakukan validasi sebelum masuk sandbox. Tren seperti Scheme is a Hoot menunjukkan bahwa bahasa kecil bisa dikompilasi ke Wasm untuk isolasi, tetapi fokus utamanya di sini adalah desain sistem yang lebih aman, bukan bahasa pemrogramannya.

Mengapa memakai sandbox Wasm

Masalah umum pada backend modern bukan hanya upload file berukuran besar, tetapi juga format dan logika yang sulit dipercaya sepenuhnya. Contohnya:

  • Parser dokumen atau metadata yang kompleks dan rawan crash.
  • Thumbnailer, extractor, atau validator file dengan dependensi pihak ketiga.
  • Template yang dapat berisi ekspresi atau fungsi terbatas.
  • User script kecil untuk transformasi data, aturan bisnis sederhana, atau filter khusus tenant.

Jika seluruh proses itu dijalankan langsung di proses utama aplikasi, satu bug dapat berdampak ke worker, request lain, atau bahkan host jika isolasinya lemah. Wasm membantu dengan model eksekusi yang lebih sempit: modul berjalan di runtime Wasm, memakai memori linear sendiri, dan hanya dapat mengakses kemampuan yang secara eksplisit diberikan host.

Poin penting: Wasm memberi capability boundary. Modul tidak otomatis bisa membaca file, membuka socket, mengakses environment variable, atau memanggil database kecuali host mengizinkannya.

Threat model: risiko apa yang benar-benar diturunkan?

Sebelum implementasi, tentukan ancaman yang ingin dikurangi. Sandbox Wasm bukan obat universal. Ia lebih cocok untuk membatasi komponen yang memang harus memproses input tak tepercaya.

Ancaman yang cocok ditangani

  • Crash atau panic pada parser/validator yang tidak stabil.
  • Loop tak berujung atau komputasi terlalu mahal dari template/script.
  • Penggunaan memori berlebihan saat mem-parse input tertentu.
  • Akses I/O yang tidak semestinya jika runtime dikonfigurasi tanpa izin tambahan.
  • Penyebaran blast radius dari bug library parsing ke proses utama aplikasi.

Ancaman yang tidak otomatis selesai dengan Wasm

  • Autentikasi dan otorisasi. Sandbox tidak menentukan siapa boleh upload atau menjalankan template.
  • Session management dan CSRF.
  • Validasi bisnis utama, misalnya apakah file memang milik user yang benar atau apakah field wajib lengkap.
  • Penyalahgunaan volume trafik tanpa rate limit di level API.
  • Supply-chain risk dari modul Wasm atau compiler yang Anda pakai.
  • Bug pada runtime Wasm itu sendiri. Sandbox mengurangi risiko, bukan menghapusnya.

Anggap Wasm sebagai lapisan isolasi tambahan untuk komponen berisiko tinggi, bukan pengganti kontrol aplikasi inti.

Kasus penggunaan yang paling masuk akal

1. Validasi upload file

Contoh: aplikasi menerima PDF, gambar, atau format kustom dari pihak luar. Daripada mengurai seluruh file di proses utama, backend melakukan pemeriksaan awal lalu mengirim file ke worker sandbox untuk:

  • Memeriksa signature/magic bytes.
  • Membaca metadata dasar.
  • Menolak struktur yang jelas rusak.
  • Menghasilkan hasil validasi yang dibatasi formatnya.

Ini berguna terutama jika parser berasal dari kode pihak ketiga atau hasil porting yang belum sepenuhnya dipercaya.

2. Parsing format tak tepercaya

Beberapa format memiliki edge case yang mudah menyebabkan konsumsi CPU besar atau perilaku tak terduga. Wasm cocok sebagai dinding pembatas untuk parser yang hanya perlu menerima bytes dan mengembalikan struktur hasil minimal.

3. Template atau user script terbatas

Jika pengguna perlu mendefinisikan aturan sederhana, Anda bisa menyediakan bahasa kecil atau DSL yang dikompilasi ke Wasm. Konteks Scheme is a Hoot menarik karena menunjukkan bahasa kecil bisa dibawa ke Wasm, tetapi prinsip amannya tetap sama: beri capability sesedikit mungkin, hindari I/O langsung, dan batasi waktu serta memori eksekusi.

Arsitektur backend yang disarankan

Pola yang paling aman biasanya bukan menjalankan Wasm langsung di request handler utama, melainkan memisahkan proses berisiko ke worker atau service khusus.

Alur tingkat tinggi

  1. Client mengunggah file atau mengirim template ke API.
  2. API melakukan validasi awal: ukuran, tipe konten, autentikasi, otorisasi, dan batas kuota.
  3. Payload disimpan sementara di object storage atau blob store internal.
  4. Job dikirim ke worker validasi.
  5. Worker memuat runtime Wasm dengan batas CPU/memori/timeout dan tanpa capability I/O yang tidak perlu.
  6. Modul menerima input minimal, menjalankan validasi/parsing, lalu mengembalikan hasil terstruktur.
  7. Worker menulis hasil ke database atau cache, lalu API menandai status sukses/gagal.

Skema komponen

Client
  -> API Gateway / Backend
      - auth, rate limit, content-length check
      - basic MIME / extension / quota validation
      - store temp object
      - enqueue validation job
          -> Sandbox Worker
              - load Wasm runtime
              - set memory/cpu/time budget
              - deny network/filesystem by default
              - pass bounded input
              - collect structured result + logs
          -> Result Store / DB
  <- API returns pending / accepted / rejected

Pemisahan ini membantu dalam beberapa hal:

  • Request web tetap responsif.
  • Kegagalan sandbox tidak langsung menjatuhkan API utama.
  • Kontrol observabilitas dan retry lebih mudah.
  • Kebijakan resource dapat berbeda antara API dan worker.

Kontrol keamanan yang wajib: CPU, memori, timeout, dan I/O

Batas CPU dan timeout

Masalah paling umum pada eksekusi kode tak tepercaya adalah pekerjaan yang tidak pernah selesai atau terlalu mahal. Anda perlu menetapkan:

  • Timeout keras: hentikan eksekusi setelah durasi tertentu.
  • Budget instruksi/fuel jika runtime mendukungnya.
  • Batas concurrency: cegah terlalu banyak sandbox berjalan bersamaan.

Timeout sebaiknya konservatif dan sesuai use case. Validator metadata ringan perlu batas jauh lebih ketat daripada parser batch internal.

Batas memori

Wasm memberi model memori yang lebih terkendali, tetapi tetap bisa meminta alokasi besar jika host mengizinkan. Karena itu:

  • Tetapkan maksimum memori per eksekusi.
  • Batasi ukuran input sebelum dikirim ke sandbox.
  • Hindari menyalin buffer besar berkali-kali antara host dan modul.
  • Waspadai format terkompresi yang membengkak saat diekstrak.

Pembatasan I/O

Secara desain, modul Wasm seharusnya tidak mendapat akses file system, network, clock, env, atau proses host kecuali dibuka secara eksplisit. Prinsip yang aman:

  • Default deny untuk seluruh I/O.
  • Jika butuh data tambahan, kirim sebagai parameter atau buffer, bukan memberi akses file system umum.
  • Jangan berikan akses network keluar hanya untuk "praktis" mengambil resource.
  • Jangan meneruskan secret, token, atau connection string ke runtime.

Jika template atau script pengguna memerlukan data, lebih aman host menyediakan fungsi terbatas seperti get_input_field(name) daripada membuka akses database atau HTTP.

Validasi sebelum masuk sandbox tetap wajib

Kesalahan umum adalah menganggap semua input bisa dilempar ke sandbox dan selesai. Padahal validasi awal justru mengurangi beban dan peluang eksploitasi.

Contoh validasi awal untuk upload

  • Batas ukuran file dan jumlah file.
  • Pemeriksaan content type yang masuk akal.
  • Pemeriksaan magic bytes/signature dasar.
  • Normalisasi nama file dan penolakan karakter/path berbahaya.
  • Pembatasan tipe file yang memang didukung.
  • Penyimpanan di lokasi sementara yang terisolasi.

Contoh validasi awal untuk template/user script

  • Batas panjang source.
  • Batas jumlah fungsi, kedalaman ekspresi, atau ukuran AST jika ada tahap kompilasi sendiri.
  • Daftar fitur yang diizinkan dan dilarang.
  • Penolakan rekursi atau makro tertentu jika model eksekusinya sulit dikontrol.

Validasi awal membuat sandbox fokus pada tugas yang memang perlu diisolasi, bukan menjadi lapisan pertama untuk semua input.

Contoh kontrak host-modul yang aman

Desain antarmuka antara host dan modul Wasm sebaiknya sempit, deterministik, dan mudah diaudit. Hindari API luas yang perlahan berubah menjadi "mini runtime" penuh capability.

// Host side pseudocode
request = {
  "kind": "image-metadata-validate",
  "max_bytes": 1048576,
  "payload_ref": "temp-object-id-123"
}

// Host membaca objek, membatasi ukuran, lalu mengirim byte ke modul.
input_bytes = read_temp_object_limited(request.payload_ref, request.max_bytes)

result = run_wasm_module({
  module: "image_validator.wasm",
  stdin: input_bytes,
  timeout_ms: 200,
  memory_limit_mb: 32,
  allow_network: false,
  allow_filesystem: false
})

if result.timed_out or result.trapped:
  mark_validation_failed("sandbox_error")
else:
  parsed = decode_json(result.stdout)
  persist_validation_result(parsed)

Kontrak seperti ini punya beberapa kelebihan:

  • Input eksplisit dan dibatasi ukurannya.
  • Modul tidak tahu lokasi file asli atau kredensial sistem.
  • Output dibatasi ke format terstruktur, misalnya JSON kecil atau kode status.
  • Kegagalan mudah dipetakan ke fallback operasional.

Desain fallback saat sandbox gagal

Sandbox pasti akan gagal pada sebagian kasus: timeout, trap, format rusak, worker overload, atau runtime error. Karena itu desain fallback tidak boleh improvisasi.

Pilihan fallback yang umum

  • Fail closed: tolak upload atau template jika validasi tak dapat diselesaikan. Cocok untuk jalur sensitif.
  • Quarantine: simpan file sebagai pending review atau pending async scan.
  • Degrade feature: terima file, tetapi jangan proses preview/metadata lanjutan sebelum lolos validasi.
  • Retry terbatas: hanya untuk error infrastruktur, bukan untuk input yang berulang kali memicu timeout.

Pilih fallback berdasarkan dampak bisnis dan keamanan. Untuk dokumen publik yang akan dibagikan ke banyak user, fail closed biasanya lebih aman. Untuk workflow internal non-kritis, mode karantina bisa lebih realistis.

Yang perlu dicatat saat fallback

  • Kategori error: timeout, memory limit, trap, decode error, infra error.
  • Hash file atau request ID, bukan isi sensitif mentah.
  • Ukuran input dan tipe operasi.
  • Tenant/user ID untuk investigasi dan rate limit.

Logging, metrik, dan rate limit

Tanpa observabilitas, sandbox mudah dianggap bekerja baik padahal diam-diam sering timeout atau dijadikan alat DoS murah.

Apa yang perlu dilog

  • ID request/job dan korelasinya.
  • Modul Wasm yang dijalankan.
  • Durasi eksekusi.
  • Status akhir: sukses, reject, timeout, trap, limit exceeded.
  • Ukuran input/output.
  • Tenant atau aktor pemicu.

Hindari logging payload mentah jika mengandung data sensitif atau file besar. Lebih aman log hash, metadata, dan ringkasan hasil.

Metrik operasional yang berguna

  • Rasio sukses vs gagal per modul.
  • Distribusi durasi eksekusi.
  • Jumlah timeout dan memory limit per tenant.
  • Kedalaman antrean worker dan tingkat retry.
  • Persentase fallback/karantina.

Rate limit tetap diperlukan

Wasm sandbox menurunkan dampak satu eksekusi, tetapi tidak mencegah penyerang mengirim ribuan input yang masing-masing mahal. Karena itu, pasang rate limit dan kuota di beberapa level:

  • Per IP atau token API.
  • Per user atau tenant.
  • Per jenis operasi, misalnya upload validasi berat vs template render ringan.
  • Per antrean worker agar backpressure jelas.

Template dan user script: kapan Wasm cocok, kapan tidak

Jika kebutuhan Anda hanya substitusi variabel sederhana seperti {{name}}, memakai Wasm sering berlebihan. Template engine yang ketat tanpa evaluasi ekspresi kompleks biasanya cukup.

Wasm lebih cocok ketika:

  • Anda memang butuh evaluasi logika kecil dari pengguna.
  • Ada beberapa tenant dengan aturan berbeda.
  • Anda ingin isolasi lebih baik daripada menjalankan script di bahasa host.
  • Anda bisa mendefinisikan ABI/kontrak fungsi yang sempit.

Sementara itu, Wasm mungkin tidak ideal jika:

  • Anda butuh akses library host yang luas.
  • State bersama dan I/O kompleks adalah kebutuhan utama.
  • Biaya serialisasi data masuk/keluar sandbox lebih besar daripada manfaatnya.

Konteks Scheme is a Hoot relevan di sini: bahasa kecil yang dikompilasi ke Wasm bisa menjadi kendaraan untuk aturan pengguna yang terbatas. Namun keamanan tetap bergantung pada desain capability, limit resource, dan kontrak host, bukan pada fakta bahwa bahasanya kecil atau "aman" secara branding.

Jebakan umum saat mengadopsi sandbox Wasm

1. Menganggap sandbox sebagai pengganti validasi utama

Wasm tidak memeriksa apakah user berhak mengunggah file tertentu, apakah ukuran file masuk akal untuk paket langganan, atau apakah payload sudah lolos aturan bisnis.

2. Memberi capability terlalu banyak

Kesalahan klasik adalah membuka file system atau network karena modul "sesekali butuh". Begitu capability melebar, manfaat isolasi berkurang drastis.

3. Menjalankan sandbox di jalur sinkron yang sensitif latensi

Untuk operasi berat, lebih aman pindahkan ke worker async. Menjalankan semuanya inline dapat membuat API lambat dan rentan antrian menumpuk.

4. Tidak membatasi input sebelum copy ke sandbox

Meski modul dibatasi 32 MB, host bisa lebih dulu kehabisan memori saat membaca file 500 MB ke buffer. Batas harus diterapkan sebelum tahap itu.

5. Mengabaikan supply chain

Modul Wasm, compiler, dan runtime adalah bagian dari rantai kepercayaan. Simpan artefak yang ditandatangani, audit asal modul, dan pin dependensi seperlunya.

6. Tidak punya kebijakan fallback

Jika timeout terjadi, apakah upload ditolak, dikarantina, atau diterima tanpa preview? Jawaban ini harus jelas sebelum produksi.

Checklist implementasi praktis

  1. Tentukan use case berisiko tinggi yang benar-benar butuh isolasi.
  2. Definisikan threat model: crash, loop, memory abuse, I/O abuse, DoS.
  3. Pisahkan worker sandbox dari API utama jika memungkinkan.
  4. Tetapkan batas ukuran input sebelum pembacaan penuh.
  5. Gunakan runtime Wasm dengan konfigurasi default deny untuk I/O.
  6. Pasang timeout, batas memori, dan batas concurrency.
  7. Desain kontrak host-modul yang sempit: input bytes terbatas, output terstruktur kecil.
  8. Lakukan validasi awal di level API: auth, quota, type allowlist, magic bytes dasar.
  9. Tambahkan logging dan metrik untuk timeout, trap, dan rasio reject.
  10. Pasang rate limit per user/tenant/operasi.
  11. Tentukan fallback: fail closed, quarantine, atau degrade feature.
  12. Uji dengan input rusak, file besar, payload terkompresi, dan script yang sengaja looping.

Strategi pengujian dan debugging

Uji yang perlu dilakukan

  • Malformed input: file dipotong di tengah, header palsu, field panjang ekstrem.
  • Resource exhaustion: input yang memicu parse lambat atau alokasi besar.
  • Concurrency test: banyak job paralel dari tenant yang sama.
  • Capability test: pastikan modul gagal saat mencoba akses I/O yang tidak diizinkan.
  • Fallback test: timeout dan trap harus menghasilkan status operasional yang benar.

Tips debugging

  • Pisahkan log host dan log modul agar mudah melihat apakah kegagalan terjadi sebelum, selama, atau sesudah eksekusi Wasm.
  • Simpan sampel input problematik dalam karantina internal yang aman untuk reproduksi.
  • Tambahkan request ID ke seluruh tahap: API, queue, worker, storage.
  • Jika modul sering timeout, evaluasi apakah masalahnya di algoritme parser, ukuran input, atau serialisasi host-modul.

Penutup

Sandbox Wasm untuk validasi upload dan template yang lebih aman masuk akal ketika aplikasi harus memproses input atau logika yang tidak sepenuhnya dipercaya. Keuntungannya nyata jika dipakai dengan model capability minimal, pembatasan CPU/memori/timeout, I/O yang ditutup default, validasi awal yang ketat, logging, rate limit, dan fallback yang jelas.

Yang paling penting, jangan menempatkan Wasm sebagai pengganti autentikasi, session, atau validasi utama. Nilainya ada pada containment: ketika parser, template, atau user script salah, dampaknya tetap sempit dan sistem utama tetap lebih mudah dipertahankan.