Episode ini mengamankan service Rust: mengaktifkan HTTPS dengan TLS dan sertifikat lewat rustls, implementasi autentikasi JWT dan session, serta proteksi input, CORS, rate limiting, dan security header dengan tower-http.

Server yang berjalan hanyalah setengah dari pekerjaan — yang membedakan service production dari demo adalah keamanan. Episode 11 mengamankan aplikasi Rust kalian dari tiga sisi: enkripsi transport dengan TLS, verifikasi identitas dengan JWT dan session, serta pertahanan di lapisan HTTP seperti CORS, rate limiting, dan security header.
Ini bukan topik tambahan. Setiap API publik menghadapi scan otomatis, brute force, dan eksploitasi umum. Dengan materi episode ini, kalian membangun fondasi keamanan yang sejalan dengan praktik industri.
TLS mengenkripsi komunikasi antara klien dan server. Untuk HTTPS, server menyajikan sertifikat yang diterbitkan Certificate Authority (CA) seperti Let's Encrypt. rustls adalah implementasi TLS murni Rust yang aman dan mudah dipakai.
Untuk pengembangan lokal, buat sertifikat self-signed dengan openssl:
openssl req -x509 -newkey rsa:2048 \
-keyout key.pem -out cert.pem \
-days 365 -nodes -subj "/CN=localhost"openssl req -x509 -newkey rsa:2048 membuat key dan sertifikat self-signed sekaligus. Di production, gunakan sertifikat dari CA — di Kubernetes sering lewat cert-manager.
Implementasi TLS murni Rust dipakai lewat axum-server yang menangani sertifikat dan handshake:
cat > src/main.rs <<'EOF'
use axum::{routing::get, Router};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "aman" }));
axum_server::bind_rustls(
std::net::SocketAddr::from(([127, 0, 0, 1], 8443)),
axum_server::tls_rustls::RustlsConfig::from_pem_file(
"cert.pem",
"key.pem",
)
.await
.unwrap(),
)
.serve(app)
.await
.unwrap();
}
EOF
cargo runRustlsConfig::from_pem_file memuat sertifikat dan key, lalu bind_rustls membungkus server dengan TLS. Uji dengan curl -k https://localhost:8443 — flag -k memungkinkan sertifikat self-signed. Tambahkan dependency dengan cargo add axum-server --features rustls. Di production, TLS sering di-handle oleh ingress atau reverse proxy seperti nginx dan traefik.
JWT (JSON Web Token) membawa klaim terenkripsi tanda tangan. Server menandatangani token saat login, klien menyimpan dan mengirimnya di header Authorization, dan server memverifikasi tanpa menyimpan session.
cat > src/main.rs <<'EOF'
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Klaim {
sub: String,
exp: usize,
}
fn main() {
let klaim = Klaim {
sub: String::from("pengguna-1"),
exp: 9999999999,
};
let token = encode(
&Header::default(),
&klaim,
&EncodingKey::from_secret(b"rahasia"),
)
.unwrap();
println!("token: {}", token);
let data = decode::<Klaim>(
&token,
&DecodingKey::from_secret(b"rahasia"),
&Validation::default(),
)
.unwrap();
println!("sub: {}", data.claims.sub);
}
EOF
cargo runencode menandatangani klaim menjadi token; decode memverifikasi tanda tangan dan membaca klaim. exp adalah waktu kedaluwarsa. cargo add jsonwebtoken menambahkan library; di production, secret disimpan di environment, bukan di kode.
Untuk aplikasi web klasik, session menyimpan state autentikasi di sisi server, sementara klien hanya memegang cookie session id. Crate seperti axum-sessions atau tower-sessions menyediakan ini, dengan storage di memori atau database.
OAuth2 membiarkan pengguna login dengan provider eksternal (Google, GitHub). Alur authorization code: aplikasi mengarahkan pengguna ke provider, menerima kode, lalu menukarnya dengan token lewat backend. Crate oauth2 menangani seluruh protokol:
use oauth2::{basic::BasicClient, AuthUrl, ClientId, TokenUrl};
let client = BasicClient::new(
ClientId::new("id-klien".to_string()),
None,
AuthUrl::new("https://provider/authorize".to_string()).unwrap(),
Some(TokenUrl::new("https://provider/token".to_string()).unwrap()),
);BasicClient menangkap konfigurasi provider. Seluruh alur: redirect ke auth_url, tukar kode di token_url, dan validasi klaim.
Cross-Origin Resource Sharing (CORS) mengatur siapa yang boleh memanggil API dari browser. tower-http menyediakan layer siap pakai:
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = Router::new()
.route("/", get(|| async { "api" }))
.layer(cors);CorsLayer menetapkan origin, method, dan header yang diizinkan. Untuk produksi, jangan pakai Any untuk origin — batasi ke domain yang benar-benar dipakai.
Rate limiting membatasi jumlah request per klien untuk menahan brute force:
let app = Router::new()
.layer(RequestBodyLimitLayer::new(1024 * 1024))
.route("/", get(|| async { "terbatas" }));Jangan lupa security headers dasar: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, dan Strict-Transport-Security saat sudah HTTPS. Di Kubernetes, header dan rate limiting sering juga di-handle ingress controller di lapisan depan.
Inti yang harus dibawa pulang:
exp.Di episode 12 selanjutnya kita akan membahas concurrency, async, dan parallelisme — model async await dengan runtime tokio, task spawning dan channel dengan tokio::sync, serta thread safety lewat trait Send dan Sync dan pola actor-like. Kalian akan memproses banyak hal bersamaan dengan aman.