Menjawab Tantangan Hardening Auth, Session, dan Secret di Bun versi Rust

Hardening Auth, Session, dan Secret di Bun versi Rust berarti menyesuaikan pola proteksi tradisional dengan runtime Bun yang sekarang berjalan di Rust. Dalam 1-2 langkah pertama, Anda perlu memahami bahwa Bun Rust mengutamakan determinisme memori, model event-driven yang sama cepat, serta kontrol akses langsung ke sistem file dan network. Artikel ini menjelaskan langkah konkret untuk memperketat autentikasi, menyimpan session dengan aman, dan mengelola secret di lingkungan Bun Rust, sekaligus menaruh perhatian pada rate limit dan penanganan upload.

Dengan memperlihatkan contoh implementasi yang kompatibel dengan bun::http dan prinsip Rust, pembaca akan melihat bagaimana arsitektur baru Bun memengaruhi pilihan validasi input, penyimpanan secret, dan skema audit.

Memahami Implikasi Rust pada Keamanan Bun

Bun versi Rust mengubah aspek deterministik runtime yang sebelumnya dialami oleh mesin JavaScript murni. Penanganan memory safety di Rust menutup kelas khas bug C/C++ seperti use-after-free, tetapi proses interop dengan API tingkat tinggi masih membutuhkan kontrol eksplisit. Karena Bun tidak lagi mengandalkan V8, developer harus menjamin struktur data autentikasi, session, dan secret tidak hanya tervalidasi tetapi juga tidak bocor saat panic atau thread unwind.

Prinsip utama:

  • Imutabilitas default memperkecil race condition pada token session karena data tidak diubah tanpa mutasi eksplisit.
  • Ownership memaksa framework memikirkan siapa yang memegang secret dalam satu scope.
  • Error handling eksplisit memastikan seluruh jalur autentikasi menangani error tanpa panik yang mengungkapkan stack trace.

Paradoxally, runtime Rust memberi peluang bagus untuk menaruh secret di memori yang dikontrol oleh scope, dan Bun menyediakan alat untuk membentengi akses ke API native.

Hardening Autentikasi di Bun Rust

Validasi Input dan Body Parsing Aman

Pastikan Anda menghindari deserialisasi langsung terhadap payload JSON tanpa validasi. Gunakan crates seperti serde dengan derive yang ketat, lalu verifikasi bentuk setelah parsing. Contoh pattern:

use bun::http::{Request, Response, StatusCode};
use serde::Deserialize;

#[derive(Deserialize)]
struct LoginPayload { username: String, password: String }

async fn handle_login(req: Request) -> Response {
    let payload = match req.json::().await {
        Ok(body) if body.username.len() >= 3 => body,
        _ => return Response::text("invalid input").with_status(StatusCode::BAD_REQUEST),
    };
    // lanjutkan autentikasi
}

Validasi panjang, pola, dan batas input mencegah credential stuffing atau payload injection. Perhatikan bahwa pemanggilan req.json() sudah asynchronous dan menangani panic, sesuai dengan model Bun Rust.

Strategy Token dan Rotate Key

Gunakan pendekatan token stateless (JWT) hanya jika Anda mengontrol signing key secara eksklusif. Caranya, simpan secret signing di file yang hanya dapat diakses oleh proses Bun melalui izin file descriptor yang dikunci, jangan di-commit ke repo.

Rotasi key dilakukan dengan membuat versi key baru dan menyimpan key version di header token Anda. Ketika Bun membaca token, ia membandingkan version tersebut ke key versi terbaru dalam HashMap yang dilindungi mutex dan diinisialisasi saat bootstrap.

Session dan Secret Handling yang Konsisten

Menyimpan Session dengan Secure Store

Bun Rust mendukung cross-thread state lewat Arc dan Mutex. Simpan session identifier di cache seperti Redis, tapi pertahankan pola berikut:

  • Hash session ID dengan HMAC sebelum dikirim ke cookie.
  • Setting HttpOnly, Secure, dan SameSite=Strict pada cookie.
  • Gunakan expires untuk invalidasi otomatis.

Pada sisi server, buat handler:

async fn set_session_cookie(res: &mut Response, session_id: &str) {
    let signed = hmac_sha256(session_id.as_bytes(), b"secret");
    res.headers_mut().insert("set-cookie", format!("session={}; HttpOnly; Secure; SameSite=Strict", signed));
}

Sign dan enkripsi session ID di scope Rust mengurangi risiko replay karena proses tidak eksplisit membocorkan secret.

Secret Management – Lingkungan Terisolasi

Bun Rust memudahkan akses environment melalui std::env, tetapi hindari menyimpan secret dalam String yang dibiarkan di heap terlalu lama. Sebaiknya, gunakan crate seperti zeroize untuk menghapus secret setelah digunakan, atau isolasi akses melalui helper:

fn load_secret(name: &str) -> SecretGuard {
    let value = std::env::var(name).expect("secret missing");
    SecretGuard::new(value)
}

struct SecretGuard(String);
impl Drop for SecretGuard {
    fn drop(&mut self) {
        zeroize::Zeroize::zeroize(&mut self.0);
    }
}

Arsitektur Bun Rust memanfaatkan lifetimes Rust untuk memastikan secret tidak keluar dari scope. Hindari memaparkan secret ke logging atau error message.

Rate Limit, Abuse Defense, dan Upload Handling

Rate Limiting di Runtime Bun

Gunakan dashmap atau store in-memory yang terhindar dari locking berat untuk menyimpan counter per API key atau IP. Karena Rust tidak memiliki GC, Anda harus mengatur cleanup secara manual:

use dashmap::DashMap;
use std::time::{Duration, Instant};

static RATE_LIMIT: Lazy> = Lazy::new(DashMap::new);

fn check_rate_limit(key: &str) -> bool {
    let now = Instant::now();
    let entry = RATE_LIMIT.entry(key.to_string()).or_insert((now, 0));
    if now.duration_since(entry.0) > Duration::from_secs(60) {
        entry.0 = now;
        entry.1 = 1;
        true
    } else if entry.1 >= 120 {
        false
    } else {
        entry.1 += 1;
        true
    }
}

Dengan memanfaatkan Instant dan DashMap, Anda menghindari blocking mutex sekaligus menjaga TTL counter tetap akurat.

Validasi Upload dan File Handling

Untuk upload, Bun Rust memanfaatkan streaming body. Validasi MIME type, ukuran payload, serta sanitasi nama file. Jangan langsung menyimpan ke disk sebelum memverifikasi:

  • Gunakan temporary buffer dengan batas ukuran.
  • Pastikan hanya extensi yang diizinkan.
  • Gunakan tokio::fs::File jika menjalankan proses asynchronous.

Penggunaan streaming memastikan memory Bun tidak penuh. Jika data terlalu besar, hentikan proses dan kembalikan 413 Payload Too Large.

Checklist Audit Backend untuk Bun Rust

Gunakan checklist berikut untuk memastikan semua aspek hardening sudah tercakup:

  1. Autentikasi: Validasi input, rate limit per endpoint, penanganan error tanpa informasi sensitif.
  2. Session: HMAC session ID, cookie flags dengan HttpOnly & Secure, rotasi session policy.
  3. Secret: Loader environment dengan zeroize atau scope management, jangan log secret.
  4. Rate Limit/Abuse: Per IP/API key counter, fallback storage seperti Redis jika needs skalabilitas.
  5. Upload: Batasi ukuran, validasi type, sanitasi path/file name.
  6. Logging dan Error: Hindari panic tersebar, sediakan middleware yang memberikan response generik.
  7. Audit: Integrasi tracing Bun untuk mencatat request ID dan hasil autentikasi.

Checklist ini membantu tim backend memastikan tidak ada aspek kritis yang terlewat saat menerapkan Bun versi Rust.

Kesimpulan dan Trade-offs

Bun versi Rust memperkenalkan kekuatan Rust dalam keamanan runtime, tetapi memerlukan pendekatan eksplisit terhadap ownership dan error handling. Hardening Auth, Session, dan Secret berarti memanfaatkan lifecycle Rust untuk membatasi exposure secret, menerapkan rate limit yang efisien, dan menjaga upload tetap terkendali. Walaupun memerlukan usaha lebih dibandingkan runtime dinamis, keuntungannya adalah determinisme memori, performa lebih stabil, dan kontrol penuh atas data sensitif.

Untuk tim yang sudah terbiasa dengan Rust, menerapkan pattern ini akan terasa natural dan aman, sementara tim yang masih baru akan mendapat keuntungan besar dari konsistensi tipe dan keamanan memori yang langsung dibawa oleh Bun Rust.