Belajar Axum - Rate Limiting & DoS Protection
Series/Belajar Axum/Episode 19
Episode 19 of 28

Belajar Axum - Rate Limiting & DoS Protection

Melindungi API dari lonjakan dan penyalahgunaan: rate limiting token bucket dengan tower-governor, throttling per-IP pada login dan endpoint publik, plus strategi timeout dan buffer untuk menahan DoS.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Fitur yang tidak terbatas adalah undangan penyalahgunaan: login bisa dibombardir tebakan password (brute-force), endpoint publik bisa dihantam bot, dan satu IP bisa menghabiskan seluruh resource. Episode ini memasang rem — rate limiting dan pertahanan DoS.

Mengapa rate limiting penting bahkan sebelum aplikasi populer? Karena penyalahgunaan tidak menunggu popularitas — crawler dan bot menyerang endpoint publik dalam hitungan menit setelah deploy. Rate limiting adalah pertahanan murah yang mencegah kerusakan mahal di kemudian hari.

Algoritma Token Bucket

tower-governor menerapkan token bucket: bucket berisi token; tiap request mengonsumsi satu token; bucket diisi ulang dengan kecepatan tetap. Request yang datang saat bucket kosong ditolak (429 Too Many Requests).

100%

Dua parameter yang menentukan perilaku:

  • fill rate — kecepatan token baru masuk per detik (kapasitas jangka panjang).
  • burst — ukuran bucket (lonjakan sesaat yang masih ditoleransi).

Misal 1 token/detik dengan burst 5: klien bisa kirim 5 request sekaligus, lalu harus menunggu 1 detik antar request. Ini melindungi dari banjir tanpa menghukum burst normal (misal refresh halaman).

Setup tower-governor

Tambah tower-governor
cargo add tower_governor --features rtrb

Contoh konfigurasi per-IP dengan burst yang wajar:

Rate limit per IP
use std::num::NonZeroU32;
use tower_governor::{
    governor::Quota,
    GovernorConfig,
    GovernorConfigBuilder,
};
 
fn rate_limit_config() -> GovernorConfig {
    GovernorConfigBuilder::default()
        .per_second(10)
        .burst_size(NonZeroU32::new(20).unwrap())
        .key_extractor(tower_governor::key_extractors::SmartIpKeyExtractor)
        .finish()
        .expect("gagal build rate limit config")
}
 
fn app() -> Router {
    let rate_limiter = tower_governor::GovernorLayer {
        config: Arc::new(rate_limit_config()),
    };
 
    Router::new()
        .route("/api/health", axum::routing::get(|| async { "ok" }))
        .layer(rate_limiter)
}
  • per_second(10) — 10 token per detik per IP.
  • burst_size(20) — lonjakan 20 request tetap diterima.
  • SmartIpKeyExtractor — mengelompokkan klien per IP (memperhatikan proxy via X-Forwarded-For; pastikan header ini tepercaya — lihat peringatan di bawah).

Request yang melewati limit mendapat 429 Too Many Requests dari governor, plus header Retry-After.

Warning

SmartIpKeyExtractor memercayai X-Forwarded-For — jika aplikasi kalian langsung terekspos publik (tanpa proxy), attacker bisa memalsukan header untuk melewati limit. Pastikan header ini hanya tepercaya dari reverse proxy kalian sendiri (episode 20), atau gunakan key extractor berbasis IP koneksi langsung.

Rate Limit pada Login: Throttling Khusus

Endpoint login perlu aturan yang lebih ketat — tebakan password (brute-force) adalah serangan paling umum:

Rate limit login
use std::num::NonZeroU32;
use tower_governor::{
    governor::Quota,
    key_extractors::SmartIpKeyExtractor,
    GovernorConfigBuilder,
};
 
fn login_rate_limit() -> GovernorConfig {
    GovernorConfigBuilder::default()
        .per_second(1)                            // 1 percobaan per detik
        .burst_size(NonZeroU32::new(5).unwrap())  // max 5 percobaan beruntun
        .key_extractor(SmartIpKeyExtractor)
        .finish()
        .expect("gagal build login rate limit")
}
 
fn auth_routes() -> Router {
    Router::new()
        .route("/auth/login", axum::routing::post(login))
        .layer(tower_governor::GovernorLayer {
            config: Arc::new(login_rate_limit()),
        })
}

Angka yang masuk akal untuk login: 3-10 percobaan per menit per IP. Terlalu longgar tidak berguna, terlalu ketat mengganggu user. Setelah throttle per-IP, pertimbangkan throttle per-akun (berdasarkan username) sebagai lapisan kedua — mencegah attacker yang menyebar dari banyak IP menebak satu akun. Kombinasi ini adalah pertahanan brute-force standar.

Tip

Bonus keamanan login: gabungkan rate limit dengan delayed response seragam dan pesan error identik (dari episode 13). Serangan yang ditebak datanya dari timing dan pesan menjadi jauh lebih sulit.

Rate Limit untuk API Publik

Untuk endpoint yang terbuka ke publik (misal /posts), pilih limit yang mengizinkan normal use tetapi menahan bot:

Rate limit API publik
fn public_api_limit() -> GovernorConfig {
    GovernorConfigBuilder::default()
        .per_second(5)
        .burst_size(NonZeroU32::new(15).unwrap())
        .key_extractor(SmartIpKeyExtractor)
        .finish()
        .unwrap()
}

Tentukan angka dari traffic nyata: ukur puncak request per IP dalam satu menit di log (episode 17) lalu setel limit sedikit di atas itu. Memilih angka tanpa data = menebak.

Strategi Tambahan untuk DoS

Rate limiting menangani abuse per-IP. DoS yang lebih masif butuh lapisan tambahan:

Timeout dari Episode 8

TimeoutLayer membatasi durasi setiap request — pencegah serangan yang "menggantung" koneksi (slowloris):

Timeout global
use std::time::Duration;
use tower_http::timeout::TimeoutLayer;
 
.layer(TimeoutLayer::new(Duration::from_secs(30)))

Request yang melebihi batas dibatalkan dan worker dibebaskan.

Per-binary Sampler untuk Request Mahal

Beberapa endpoint mahal (query berat, kompresi besar) bisa disampling untuk batasan lebih ketat:

Batas request mahal
use std::time::Duration;
use tower_http::timeout::TimeoutLayer;
 
fn heavy_routes() -> Router {
    Router::new()
        .route("/reports/export", axum::routing::get(export_report))
        .layer(TimeoutLayer::new(Duration::from_secs(120)))
        // rate limit lebih rendah untuk endpoint mahal
        .layer(tower_governor::GovernorLayer {
            config: Arc::new(heavy_endpoint_limit()),
        })
}
 
fn heavy_endpoint_limit() -> tower_governor::GovernorConfig {
    tower_governor::GovernorConfigBuilder::default()
        .per_second(1)
        .burst_size(NonZeroU32::new(2).unwrap())
        .key_extractor(SmartIpKeyExtractor)
        .finish()
        .unwrap()
}

Endpoint ekspor besar yang memakan waktu 2 detik CPU per request tidak boleh melayani 1000 request/menit dari satu IP — throttle dengan limit yang proporsional terhadap biayanya.

Batas Koneksi: Lapisan Infrastruktur

Pada titik tertentu, pertahanan DoS berpindah dari aplikasi ke infrastruktur — reverse proxy membatasi koneksi konkuren, CDN menyerap banjir bandwidth. Episode 20 dan 24 membahas arsitektur ini; di episode ini yang penting: aplikasi punya timeout + rate limit sebagai pertahanan pertama.

Praktik: Stack Perlindungan Lengkap

Gabungkan semuanya pada satu aplikasi — inilah "pertahanan berlapis" untuk traffic masuk:

Lapisan proteksi
use std::sync::Arc;
use std::time::Duration;
use tower_http::timeout::TimeoutLayer;
 
fn protected_app() -> Router {
    Router::new()
        .merge(auth_routes())          // rate limit login (ketat)
        .merge(public_api())           // rate limit API publik
        .merge(heavy_routes())         // timeout + limit khusus
        .layer(TimeoutLayer::new(Duration::from_secs(30)))
}

Urutan eksekusi (dari luar ke dalam): timeout global → rate limit login/publik → handler. Setiap lapisan menangani jenis serangan yang berbeda:

LapisanMenangani
TimeoutRequest menggantung, slowloris
Rate limitBrute-force, bot, abuse per-IP
Body limit (episode 18)Request raksasa
Auth & RBAC (episode 13)Akses ilegal

Penutup

Pada episode 19 ini aplikasi kalian punya rem dan perisai:

  • Token bucket dengan tower-governor: per_second + burst_size.
  • Rate limit ketat pada login (brute-force) dan moderat pada API publik.
  • SmartIpKeyExtractor per-IP, dengan peringatan header X-Forwarded-For.
  • Timeout global + limit khusus untuk endpoint mahal.
  • Pertahanan berlapis: timeout → rate limit → body limit → auth.

Di episode 20 selanjutnya kita amankan transport: HTTPS & TLS — rustls dan axum-server, deployment di belakang reverse proxy (nginx/caddy), serta sertifikat yang benar. Sampai jumpa di episode 20!

Belajar Axum - Rate Limiting & DoS Protection | Belajar Axum