Episode ini memperkuat transport dan header: memasang TLS dengan redirect HTTP ke HTTPS, menegakkan atribut cookie Secure HttpOnly dan SameSite, serta menyusun Content-Security-Policy untuk halaman enrollment yang memuat QR code sensitif.

Kode TOTP adalah rahasia yang diketik pengguna setiap 30 detik. Rahasia sekecil itu bisa diculik di tengah jalan jika transport tidak diamankan. Episode 13 membahas hardening transport: TLS di produksi, atribut cookie yang benar, dan Content-Security-Policy untuk halaman yang menampilkan QR.
Ketiga lapis ini bekerja pada lapisan yang berbeda — TLS melindungi data di jaringan, cookie melindungi session di browser, dan CSP membatasi apa yang boleh dimuat halaman. Kalian akan memasang ketiganya dan memahami mengapa aplikasi 2FA tanpa hardening ini seperti gembok di pintu yang dindingnya berlubang.
Kode TOTP yang dikirim lewat HTTP polos bisa dibaca siapa pun di jalur antara pengguna dan server. Untuk aplikasi 2FA, HTTPS bukan perbaikan — ia prasyarat. Pasang sertifikat TLS dari Let's Encrypt lewat certbot atau gunakan reverse proxy seperti Caddy yang mengelola sertifikat otomatis:
sudo apt install certbot
sudo certbot --nginx -d auth.devvnull.dev
sudo certbot renew --dry-runPerintah certbot renew --dry-run memverifikasi bahwa pembaruan otomatis sertifikat berjalan. Sertifikat yang kedaluwarsa adalah penyebab umum aplikasi tiba-tiba gagal autentikasi.
Selain server yang menyajikan HTTPS, pastikan permintaan internal antar service tidak melewati protokol yang tidak terenkripsi — secret 2FA yang bergerak antar komponen juga rahasia yang layak dilindungi.
Semua lalu lintas HTTP harus diarahkan ke HTTPS. Di Express, redirect sederhana memaksa browser pindah ke versi aman:
app.use((req, res, next) => {
if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
return next();
}
return res.redirect('https://' + req.headers.host + req.url);
});Middleware memeriksa header x-forwarded-proto untuk kasus aplikasi di belakang reverse proxy. Di Next.js, cukup konfigurasi redirect permanen pada level framework atau edge.
Perhatian khusus: di environment development, flag secure cookie boleh dinonaktifkan agar HTTPS lokal tidak wajib, tetapi di produksi redirect dan cookie secure tidak boleh bisa dimatikan lewat konfigurasi yang mudah terlewat.
Cookie session harus memakai tiga atribut sekaligus. Secure hanya mengizinkan cookie dikirim lewat HTTPS. HttpOnly menyembunyikannya dari JavaScript sehingga tahan terhadap XSS. SameSite membatasi pengiriman cookie pada permintaan lintas situs untuk menangkis CSRF:
app.use(session({
name: 'sid',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 15 * 60 * 1000
}
}));Nilai sameSite: 'lax' adalah keseimbangan yang baik: navigasi normal tetap membawa cookie, sementara permintaan POST lintas situs yang jahat tidak. Gunakan strict hanya jika alur aplikasi tidak membutuhkan cookie pada navigasi eksternal.
Attribute maxAge juga perlu disesuaikan: semakin pendek, semakin kecil jendela penyalahgunaan cookie curian. Session biasa 15 menit, sementara state pendingMfa jauh lebih pendek lagi.
Pertimbangkan cookie terpisah untuk state yang sangat sensitif, seperti pendingMfa pada episode 8. Cookie MFA yang pendek dan terisolasi mengurangi permukaan jika session utama bocor. Episode 15 akan memakai pola ini untuk trusted device.
Halaman Enable 2FA memuat QR code dan secret — dua hal yang tidak boleh diculik oleh script dari sumber asing. Content-Security-Policy membatasi dari mana script, style, dan gambar boleh dimuat:
Content-Security-Policy: default-src 'self';
script-src 'self';
img-src 'self' data:;
style-src 'self' 'unsafe-inline';
frame-ancestors 'none'Aturan img-src 'self' data: mengizinkan gambar QR yang dikirim sebagai data URL. frame-ancestors 'none' mencegah halaman kalian ditampilkan di dalam iframe situs lain — menutup celah clickjacking.
CSP berlaku per halaman. Halaman yang memuat secret 2FA bisa memakai kebijakan yang lebih ketat dari halaman publik: tanpa skrip eksternal, tanpa style dari CDN, dan tanpa embed apa pun. Semakin sedikit sumber yang diizinkan, semakin sempit ruang serangan.
Terapkan juga header Referrer-Policy dan X-Content-Type-Options sebagai kebiasaan default; keduanya mencegah kebocoran URL pada referrer dan snifing tipe konten pada halaman yang memuat data sensitif.
Episode 13 mengeraskan transport dan header: TLS dengan redirect dari HTTP, cookie session dengan atribut Secure, HttpOnly, dan SameSite, serta Content-Security-Policy yang membatasi sumber halaman enrollment.
Inti yang harus dibawa pulang:
Di episode 14 selanjutnya kita akan membahas OWASP dan best practices keamanan — verifikasi di sisi server saja, timing-safe comparison, larangan log secret dan kode, serta alasan jangan pernah menulis TOTP sendiri dari nol.