CSRF di Go

Tentu, mari kita bedah secara lengkap apa itu CSRF dan fungsi dari setiap bagian kode csrf.go ini.

## Apa itu CSRF? 🤔

CSRF adalah singkatan dari Cross-Site Request Forgery. Ini adalah jenis serangan siber di mana penyerang menipu korban (pengguna yang sudah login di suatu website) untuk tanpa sadar mengirimkan permintaan berbahaya ke website tersebut.

Analogi Sederhana:

Bayangkan Anda memiliki rekening di Bank A dan sudah login di browser Anda. Sesi login Anda disimpan dalam bentuk cookie.

Kemudian, Anda membuka tab baru dan mengunjungi website jahat, Website B. Di Website B, ada sebuah tombol yang terlihat tidak berbahaya, misalnya "Klik untuk melihat kucing lucu". Saat Anda mengkliknya, di balik layar, Website B sebenarnya mengirimkan formulir tersembunyi ke Bank A untuk mentransfer uang dari rekening Anda.

Karena Anda masih login di Bank A (browser Anda secara otomatis menyertakan cookie login), Bank A mengira permintaan transfer itu sah dari Anda dan memprosesnya. Anda baru saja menjadi korban CSRF.

Singkatnya, penyerang "memalsukan" atau "menumpang" sesi login Anda untuk melakukan aksi atas nama Anda tanpa persetujuan Anda.


## Penjelasan Kode csrf.go

Kode ini menyediakan sebuah mekanisme perlindungan bawaan di Go untuk melawan serangan CSRF. Cara kerjanya adalah dengan memeriksa dari mana sebuah permintaan berasal sebelum memprosesnya.

### Komentar Awal dan struct CrossOriginProtection

Go

// CrossOriginProtection implements protections against [Cross-Site Request
// Forgery (CSRF)] by rejecting non-safe cross-origin browser requests.
// ...
type CrossOriginProtection struct {
	bypass    atomic.Pointer[ServeMux]
	trustedMu sync.RWMutex
	trusted   map[string]bool
	deny      atomic.Pointer[Handler]
}
  • Komentar: Komentar di awal menjelaskan tujuan utama dari kode ini: melindungi dari CSRF dengan menolak permintaan cross-origin (dari situs lain) yang tidak aman.

  • CrossOriginProtection: Ini adalah struct atau cetak biru utama yang menampung semua konfigurasi untuk perlindungan CSRF.

    • bypass: Menyimpan pola-pola URL yang diizinkan untuk melewati pemeriksaan keamanan. Menggunakan atomic.Pointer agar aman diakses dari banyak request secara bersamaan (concurrency-safe).

    • trustedMu dan trusted: trusted adalah sebuah map (kamus) untuk menyimpan daftar origin (situs) yang dipercaya. trustedMu (sync.RWMutex) adalah "kunci" yang memastikan hanya satu proses yang bisa mengubah daftar ini pada satu waktu, sehingga aman dari race condition.

    • deny: Menyimpan sebuah Handler khusus yang akan dijalankan jika sebuah request diblokir. Ini juga menggunakan atomic.Pointer agar concurrency-safe.

### Fungsi-Fungsi Konfigurasi

Fungsi-fungsi ini digunakan untuk mengatur cara kerja perlindungan.

  • NewCrossOriginProtection()

    Go

    func NewCrossOriginProtection() *CrossOriginProtection {
        return &CrossOriginProtection{}
    }

    Fungsi sederhana untuk membuat objek CrossOriginProtection baru yang kosong.

  • AddTrustedOrigin(origin string)

    Go

    func (c *CrossOriginProtection) AddTrustedOrigin(origin string) error {
        // ... (validasi input)
        c.trustedMu.Lock()
        defer c.trustedMu.Unlock()
        // ... (menambahkan origin ke map trusted)
    }

    Kegunaan: Untuk mendaftarkan situs lain sebagai situs yang dipercaya. Misalnya, jika website Anda api.myapp.com dan Anda ingin mengizinkan permintaan dari webapp.myapp.com, Anda akan menambahkan webapp.myapp.com sebagai trusted origin.

  • AddInsecureBypassPattern(pattern string)

    Go

    func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
        // ...
    }

    Kegunaan: Untuk membuat pengecualian berdasarkan pola URL. Misalnya, Anda mungkin ingin menonaktifkan perlindungan CSRF untuk endpoint /api/public/ yang memang dirancang untuk bisa diakses dari mana saja. Kata Insecure ditambahkan untuk mengingatkan developer bahwa ini melonggarkan keamanan.

  • SetDenyHandler(h Handler)

    Go

    func (c *CrossOriginProtection) SetDenyHandler(h Handler) {
        // ...
    }

    Kegunaan: Untuk mengganti respons standar saat request diblokir. Secara default, server akan mengirimkan respons 403 Forbidden. Dengan fungsi ini, Anda bisa menampilkan halaman error kustom atau mencatat log khusus.

### Logika Inti Pemeriksaan

Ini adalah bagian terpenting di mana pemeriksaan keamanan sebenarnya terjadi.

  • Check(req *Request)

    Go

    func (c *CrossOriginProtection) Check(req *Request) error {
        // ...
    }

    Fungsi ini adalah otak dari perlindungan CSRF. Ia menjalankan serangkaian pemeriksaan:

    1. Metode Aman: Jika metode request adalah GET, HEAD, atau OPTIONS, maka permintaan itu dianggap "aman" (tidak mengubah data di server) dan selalu diizinkan.

    2. Sec-Fetch-Site Header: Ini adalah header modern yang dikirim oleh browser. Isinya memberitahu server asal-usul permintaan (same-origin, cross-site, dll.). Jika nilainya same-origin (dari situs yang sama), permintaan diizinkan. Jika nilainya cross-site, permintaan akan ditolak (kecuali ada di daftar pengecualian).

    3. Origin Header: Jika Sec-Fetch-Site tidak ada (mungkin karena browser lama), kode ini akan memeriksa header Origin. Ia membandingkan nilai Origin dengan Host dari request. Jika cocok, permintaan dianggap same-origin dan diizinkan.

    4. Pengecualian: Terakhir, fungsi isRequestExempt dipanggil untuk memeriksa apakah request cocok dengan pola bypass atau berasal dari origin yang terpercaya.

  • isRequestExempt(req *Request)

    Go

    func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
        // ...
    }

    Fungsi pembantu yang memeriksa dua hal:

    1. Apakah URL request cocok dengan salah satu pola yang didaftarkan di AddInsecureBypassPattern?

    2. Apakah header Origin dari request ada di dalam daftar trusted yang diatur via AddTrustedOrigin?

      Jika salah satunya benar, fungsi ini mengembalikan true dan request diizinkan.

### Middleware Handler

  • Handler(h Handler)

    Go

    func (c *CrossOriginProtection) Handler(h Handler) Handler {
        return HandlerFunc(func(w ResponseWriter, r *Request) {
            if err := c.Check(r); err != nil {
                // ... (tolak request)
                return
            }
            h.ServeHTTP(w, r)
        })
    }

    Kegunaan: Ini adalah cara paling umum untuk menggunakan CrossOriginProtection. Fungsi ini bertindak sebagai middleware atau "pembungkus". Ia mengambil handler asli Anda (h), lalu mengembalikannya dalam versi baru. Versi baru ini akan menjalankan c.Check(r) terlebih dahulu.

    • Jika Check gagal (mengembalikan error), request akan langsung ditolak dengan status 403 Forbidden.

    • Jika Check berhasil, request akan diteruskan ke handler asli Anda untuk diproses seperti biasa.

Last updated