Belajar Gin - RBAC, OAuth2 & Security Middleware
Series/Belajar Gin/Episode 14
Episode 14 of 23

Belajar Gin - RBAC, OAuth2 & Security Middleware

Episode ini melengkapi sisi keamanan: middleware RBAC dan permission, integrasi OAuth2 dan OpenID Connect dengan Keycloak, security headers bergaya helmet, perlindungan CSRF untuk form, serta rate limiting dengan golang.org/x/time/rate.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Autentikasi menjawab pertanyaan "siapa pengguna ini". Episode 14 ini menjawab pertanyaan berikutnya: apa yang boleh ia lakukan? Kalian akan membedah otorisasi berbasis peran (RBAC), integrasi OAuth2 dan OpenID Connect dengan Keycloak, serta kumpulan security middleware: CORS, security headers bergaya helmet, proteksi CSRF untuk form, dan rate limiting yang matang.

Kenapa penting? Mengizinkan semua pengguna melakukan semua operasi adalah resep bencana. Ditambah lagi, header yang salah atau CORS yang terlalu terbuka bisa membuka celah yang dieksploitasi dari browser. Middleware yang kalian pasang di sini menutup celah-celah itu satu per satu.

Middleware RBAC dan Permission

Peta Peran dan Permission

RBAC memetakan peran ke daftar permission:

Definisi peran dan permission
type Permission string
 
const (
    PermUserRead  Permission = "user:read"
    PermUserWrite Permission = "user:write"
)
 
var rolePermissions = map[string][]Permission{
    "admin": {PermUserRead, PermUserWrite},
    "editor": {PermUserRead},
}

rolePermissions memetakan peran seperti admin dan editor ke daftar permission yang boleh dilakukan. Struktur ini mudah dibaca dan diperluas: menambah peran baru cukup menambah entri di map.

Middleware requirePermission

Middleware memeriksa peran pengguna lalu mencocokkan permission:

Middleware permission
func requirePermission(perm Permission) gin.HandlerFunc {
    return func(c *gin.Context) {
        role := c.GetString("role")
        allowed := false
        for _, p := range rolePermissions[role] {
            if p == perm {
                allowed = true
                break
            }
        }
        if !allowed {
            c.AbortWithStatusJSON(403, gin.H{"error": "akses ditolak"})
            return
        }
        c.Next()
    }
}
 
r.PUT("/users/:id", authMiddleware(), requirePermission(PermUserWrite), updateUser)

c.GetString("role") mengambil peran yang disimpan middleware autentikasi dari episode 13. Jika pengguna tidak punya permission yang diminta, middleware mengembalikan 403 Forbidden. Perhatikan urutan: autentikasi dulu (siapa), baru otorisasi (boleh apa).

OAuth2 dan OpenID Connect

Mengalihkan ke Otorisasi

OAuth2 memindahkan otentikasi ke penyedia seperti Keycloak, Google, atau GitHub. Siapkan config lalu alihkan pengguna:

Install OAuth2
go get golang.org/x/oauth2
Config OAuth2 Keycloak
oauthCfg := &oauth2.Config{
    ClientID:     "belajar-gin",
    ClientSecret: "client-rahasia",
    RedirectURL:  "http://localhost:8080/auth/callback",
    Endpoint: oauth2.Endpoint{
        AuthURL:  "https://keycloak.example.com/realms/dev/protocol/openid-connect/auth",
        TokenURL: "https://keycloak.example.com/realms/dev/protocol/openid-connect/token",
    },
    Scopes: []string{"openid", "profile"},
}
 
r.GET("/auth/login", func(c *gin.Context) {
    url := oauthCfg.AuthCodeURL("state-acak")
    c.Redirect(302, url)
})

oauthCfg.AuthCodeURL("state-acak") menghasilkan URL penyedia OAuth2 tempat pengguna login. Parameter state mencegah serangan CSRF pada alur OAuth — selalu gunakan nilai acak dan verifikasi di callback.

Security Headers

Middleware Header Keamanan

Browser menghormati sejumlah header yang mengurangi serangan:

Security headers middleware
func securityHeaders() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Header("X-Content-Type-Options", "nosniff")
        c.Header("X-Frame-Options", "DENY")
        c.Header("Referrer-Policy", "no-referrer")
        c.Header("Content-Security-Policy", "default-src 'self'")
        c.Next()
    }
}

c.Header("Content-Security-Policy", "default-src 'self'") membatasi sumber konten yang boleh dimuat halaman — pertahanan utama terhadap XSS. Kombinasi nosniff, X-Frame-Options: DENY, dan Referrer-Policy memangkas permukaan serangan dari browser. Untuk HTTPS, tambahkan Strict-Transport-Security seperti akan dibahas di episode 15.

CSRF untuk Form

Token CSRF Berbasis Sesi

Form web rentan terhadap request lintas situs. Proteksi paling umum adalah synchronizer token:

Set token CSRF
func setCSRF(c *gin.Context) {
    token := csrfToken() // acak, disimpan di session
    c.SetCookie("csrf_token", token, 3600, "/", "", true, true)
    c.HTML(200, "form.html", gin.H{"csrf_token": token})
}

c.SetCookie("csrf_token", token, 3600, "/", "", true, true) menulis token ke cookie dengan flag Secure dan HttpOnly. Saat form dikirim, c.Cookie("csrf_token") dibandingkan dengan token form memakai perbandingan waktu konstan. Situs jahat tidak bisa membaca cookie karena flag HttpOnly, sehingga tidak bisa memalsukan token. Untuk API yang memakai cookie session, ini wajib dipasang — kombinasi CSRF dan cookie tanpa proteksi sangat berbahaya.

Rate Limiting dengan golang.org/x/time/rate

Middleware Rate Limit Global

Episode 12 membangun limiter per IP. Untuk versioning yang lebih matang, pakai limiter dengan fallback per IP dan key pemisah:

Rate limit per IP dengan map berlapis
var (
    limitMu   sync.Mutex
    limiters  = make(map[string]*rate.Limiter)
    interval  = rate.Every(time.Minute)
)
 
func rateLimit() gin.HandlerFunc {
    return func(c *gin.Context) {
        ip := c.ClientIP()
        limitMu.Lock()
        lim, ok := limiters[ip]
        if !ok {
            lim = rate.NewLimiter(interval, 60)
            limiters[ip] = lim
        }
        limitMu.Unlock()
 
        if !lim.Allow() {
            c.AbortWithStatusJSON(429, gin.H{"error": "terlalu banyak request"})
            return
        }
        c.Next()
    }
}

rate.NewLimiter(interval, 60) membolehkan 60 request per menit per IP dengan burst 60. c.ClientIP() memakai engine trusted proxies — pastikan dikonfigurasi benar (episode 15) agar IP tidak mudah dipalsukan. Bersihkan map secara berkala agar limiter IP lama tidak menumpuk.

Penutup

Inti yang harus dibawa pulang:

  • RBAC memetakan peran ke daftar permission; middleware memeriksa c.GetString("role").
  • OAuth2 mengalihkan login ke penyedia; Exchange menukar code dengan token.
  • Security headers seperti CSP dan nosniff memangkas serangan dari browser.
  • CSRF token dalam form + cookie HttpOnly mencegah request lintas situs.
  • rate.Limiter membatasi request per IP dengan aman.
  • Selalu pasang autentikasi sebelum otorisasi di chain middleware.

Di episode 15 selanjutnya kita akan membedah HTTPS, trusted proxies & input hardening — mengonfigurasi trusted proxies dengan benar, menjalankan TLS, integrasi reverse proxy nginx dan caddy, serta pagination dan pencegahan injection serta SSRF.

Belajar Gin - RBAC, OAuth2 & Security Middleware | Belajar Gin