Episode ini memperkuat pertahanan aplikasi: CORS dengan rs/cors, security headers CSP dan HSTS, perlindungan CSRF, body limit, dan rate limiting. Kalian juga mempelajari validasi dan sanitasi input, pencegahan injection dan SSRF, serta cara aman menangani RealIP dan X-Forwarded-For.

Autentikasi hanya setengah perjuangan. Episode 14 menutup sisi lain: middleware keamanan dan hardening input. Di sinilah aplikasi kalian belajar menolak request berbahaya sejak pintu masuk — bukan hanya saat user tidak login. Topik yang dibahas mencakup CORS untuk akses browser, security headers, proteksi CSRF, batasan ukuran body, rate limiting, validasi input, serta pencegahan injection dan SSRF. Setelah episode ini, aplikasi kalian bukan hanya berfungsi — ia juga tidak mudah ditembus.
go get github.com/rs/corsc := cors.New(cors.Options{
AllowedOrigins: []string{"https://app.example.com"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
MaxAge: 86400,
})
r.Use(c.Handler)cors.New(cors.Options{...}) membatasi origin, method, dan header yang diizinkan. c.Handler menjadi middleware chi — dipasang dengan r.Use seperti middleware lain.
AllowedOrigins: []string{"*"} memudahkan development tapi berbahaya di production. Selalu daftarkan origin yang dikenal, atau ambil dari environment variable. Gabungkan dengan middleware.RealIP bila server berada di belakang proxy.
Security headers melindungi klien di sisi browser:
func securityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Security-Policy",
"default-src 'self'")
w.Header().Set("Strict-Transport-Security",
"max-age=31536000; includeSubDomains")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("Referrer-Policy", "no-referrer")
next.ServeHTTP(w, req)
})
}w.Header().Set("Content-Security-Policy", "default-src 'self'") mengatur sumber konten yang diizinkan browser. HSTS (Strict-Transport-Security) memaksa koneksi HTTPS untuk semua kunjungan berikutnya. Empat nilai yang wajib diingat: Content-Security-Policy mengurangi XSS, Strict-Transport-Security memaksa HTTPS, X-Frame-Options: DENY mencegah clickjacking, dan nosniff mencegah browser menebak tipe MIME.
Untuk mutasi state di aplikasi web, pastikan request berasal dari halaman kalian:
if req.Method == http.MethodPost ||
req.Method == http.MethodPut ||
req.Method == http.MethodDelete {
origin := req.Header.Get("Origin")
if origin != "" && origin != appOrigin {
http.Error(w, "origin tidak dikenali",
http.StatusForbidden)
return
}
}Pengecekan Origin adalah lapisan CSRF dasar untuk API JSON. Untuk form klasik, pertimbangkan token CSRF per session. req.Header.Get("Origin") memberi tahu dari mana permintaan berasal.
Cegah request raksasa menghabiskan memory:
func limitBody(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
req.Body = http.MaxBytesReader(w, req.Body, 1<<20)
next.ServeHTTP(w, req)
})
}http.MaxBytesReader(w, req.Body, 1<<20) membatasi body maksimal 1 MiB. Saat handler membaca body melebihi batas, operasi gagal dengan http: request body too large.
r.Use(middleware.ThrottleBacklog(
100, // konteks aktif maksimal
1000, // backlog maksimal
30*time.Second,
))middleware.ThrottleBacklog(100, 1000, 30*time.Second) menolak request saat 100 request aktif dan antrian 1000 penuh selama 30 detik. Untuk limit berbasis IP per menit, pasangkan dengan Redis seperti di episode 19.
Jangan percaya input — validasi sebelum dipakai:
func validateUser(u User) error {
if u.Name == "" {
return NewAPIError(http.StatusBadRequest, "nama wajib diisi")
}
if !validEmail(u.Email) {
return NewAPIError(http.StatusBadRequest, "email tidak valid")
}
return nil
}NewAPIError(http.StatusBadRequest, ...) menolak input sejak awal dengan pesan yang jelas — lebih baik tolak di edge daripada bermasalah di database.
SQL injection sudah tercegah oleh query parameterized ($1, $2) seperti episode 9. Untuk SSRF, jangan pernah membiarkan user mengontrol URL yang di-request server:
allowedHosts := map[string]bool{
"api.internal.example.com": true,
}
host := chi.URLParam(req, "host")
if !allowedHosts[host] {
http.Error(w, "host tidak diizinkan",
http.StatusForbidden)
return
}allowedHosts[host] membatasi host yang boleh diakses server. Selalu validasi terhadap allowlist — SSRF sering muncul dari URL yang dibentuk dari input user tanpa pemeriksaan.
Header X-Forwarded-For bisa dipalsukan klien. pakai middleware.RealIP yang hanya memercayai proxy yang dikenal:
r.Use(middleware.RealIP)middleware.RealIP mengambil IP asli dari header proxy dan menimpanya ke req.RemoteAddr. Tanpa middleware ini, log dan rate limiting memakai IP yang bisa dipalsukan — celah umum pada aplikasi di belakang proxy. Pasang RealIP sebelum middleware lain yang membutuhkan IP, seperti logging dan rate limit.
Inti yang harus dibawa pulang:
http.MaxBytesReader membatasi ukuran body request.middleware.ThrottleBacklog menahan lonjakan beban.middleware.RealIP mencegah pemalsuan X-Forwarded-For.Di episode 15 selanjutnya kita mengamankan transportasi: HTTPS, reverse proxy, dan advanced networking — TLS manual, Let's Encrypt dengan autocert dan ACME, HTTP/2, reverse proxy di subrouter, serta integrasi dengan nginx dan Caddy.