CORS untuk Pemula: Bukan Serangan, Tapi Salah Konfigurasinya...
CORS untuk Pemula: Bukan Serangan, Tapi Salah Konfigurasinya Bisa Fatal
Berbeda dengan XSS yang jelas-jelas sebuah serangan, CORS sebenarnya adalah mekanisme keamanan browser. Masalahnya muncul ketika developer salah mengonfigurasinya — dan celah itu bisa membocorkan data pengguna. Ini penjelasan lengkapnya untuk pemula.
Setelah membahas XSS, banyak pembaca bertanya: topik apa lagi yang wajib dipahami developer web pemula? Salah satu jawabannya adalah CORS.
Tapi ada satu hal penting yang perlu diluruskan sejak awal: CORS bukan jenis serangan. Berbeda dengan XSS atau SQL Injection yang memang merupakan teknik penyerangan, CORS adalah mekanisme keamanan yang dibangun di dalam browser. Yang berbahaya bukan CORS-nya, melainkan konfigurasi CORS yang dibuat sembarangan oleh developer.
Artikel ini akan membedah CORS dari nol — mulai dari alasan kemunculannya, cara kerjanya, sampai kesalahan konfigurasi yang paling sering ditemukan di lapangan.
Semuanya Bermula dari Same-Origin Policy
Sebelum memahami CORS, kita harus memahami aturan yang lebih tua: Same-Origin Policy (SOP).
SOP adalah aturan bawaan browser yang berbunyi kira-kira begini: sebuah halaman web hanya boleh membaca data dari origin yang sama dengan dirinya.
Lalu apa itu "origin"? Origin adalah kombinasi tiga hal:
- Skema (
httpatauhttps) - Host (nama domain)
- Port
Ketiganya harus sama persis. Perhatikan contoh berikut, dengan acuan https://yowisben.com:
| URL | Origin sama? | Alasan |
|---|---|---|
https://yowisben.com/blog |
Ya | Hanya path yang berbeda |
http://yowisben.com |
Tidak | Skema berbeda |
https://api.yowisben.com |
Tidak | Subdomain dianggap host berbeda |
https://yowisben.com:8080 |
Tidak | Port berbeda |
Kenapa aturan ini penting? Bayangkan Anda sedang login di aplikasi bank melalui browser. Lalu di tab lain Anda membuka situs acak yang ternyata jahat. Tanpa SOP, JavaScript di situs jahat itu bisa memanggil API bank Anda, dan karena cookie sesi Anda otomatis ikut terkirim, ia bisa membaca saldo, riwayat transaksi, bahkan data pribadi Anda.
SOP-lah yang mencegah skenario mimpi buruk tersebut.
Catatan: SOP memblokir pembacaan respons, bukan selalu pengiriman request. Ini perbedaan halus yang akan kita bahas lagi nanti, dan menjadi alasan kenapa CSRF tetap bisa terjadi.
Lalu Kenapa CORS Dibutuhkan?
Masalahnya, SOP terlalu ketat untuk arsitektur aplikasi modern.
Hari ini sangat lumrah kita memisahkan frontend dan backend:
- Frontend di
https://app.perusahaan.com - Backend API di
https://api.perusahaan.com
Keduanya milik satu perusahaan, tapi menurut SOP mereka adalah origin yang berbeda. Tanpa jalan keluar, frontend Anda tidak akan bisa mengambil data dari API-nya sendiri.
Di sinilah CORS (Cross-Origin Resource Sharing) masuk. CORS adalah mekanisme untuk melonggarkan SOP secara terkendali. Intinya sederhana:
Server yang menentukan siapa saja yang boleh membaca responsnya, dan browser yang menegakkan aturan itu.
Perhatikan kalimat di atas baik-baik, karena itulah inti dari seluruh konsep CORS.
Cara Kerja CORS Secara Praktis
Simple Request
Untuk request sederhana (misalnya GET biasa), alurnya begini:
- Browser mengirim request sambil menyertakan header
Origin:
GET /api/produk HTTP/1.1
Host: api.perusahaan.com
Origin: https://app.perusahaan.com
- Server merespons dengan header izin:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.perusahaan.com
Content-Type: application/json
- Browser membandingkan keduanya. Jika cocok, JavaScript boleh membaca respons. Jika tidak cocok, browser memblokir akses dan menampilkan error CORS di console.
Preflight Request
Untuk request yang berpotensi mengubah data (PUT, DELETE, PATCH, atau POST dengan Content-Type: application/json), browser melakukan langkah pengaman tambahan: mengirim request OPTIONS terlebih dahulu untuk "bertanya izin".
OPTIONS /api/produk/12 HTTP/1.1
Origin: https://app.perusahaan.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization
Server menjawab:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.perusahaan.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 600
Baru setelah izin didapat, browser mengirim request DELETE yang sesungguhnya. Access-Control-Max-Age membuat browser menyimpan hasil izin ini selama beberapa waktu agar tidak bertanya berulang-ulang.
Tiga Kesalahpahaman Paling Umum
1. "CORS melindungi server saya."
Tidak. CORS ditegakkan oleh browser, bukan oleh server. Penyerang yang menggunakan curl, Postman, atau skrip Python sama sekali tidak terpengaruh aturan CORS. CORS melindungi pengguna dari situs jahat, bukan melindungi API Anda dari penyerang langsung.
2. "Kalau ada error CORS, berarti request-nya gagal."
Belum tentu. Pada banyak kasus request sudah sampai ke server dan sudah dieksekusi — browser hanya menolak memberikan responsnya ke JavaScript. Jadi jangan pernah mengandalkan CORS untuk mencegah aksi berbahaya.
3. "CORS bisa menggantikan autentikasi."
Tidak sama sekali. CORS mengatur siapa yang boleh membaca respons di browser. Otorisasi tetap harus dilakukan di sisi server melalui token, session, dan pengecekan hak akses.
Salah Konfigurasi yang Berbahaya
Inilah bagian yang membuat CORS relevan dibahas dalam kategori keamanan.
Memantulkan Origin secara membabi buta
Ini adalah pola paling berbahaya, dan sayangnya sering ditemukan:
// JANGAN LAKUKAN INI
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Credentials', 'true');
next();
});
Kode di atas menjawab "boleh" kepada siapa pun yang bertanya, sambil mengizinkan pengiriman cookie. Artinya, situs mana pun yang dikunjungi korban dapat memanggil API Anda dengan sesi korban yang aktif, lalu membaca hasilnya. Efeknya setara dengan kebocoran data akun.
Spesifikasi CORS sebenarnya melarang kombinasi
Access-Control-Allow-Origin: *denganAllow-Credentials: true. Memantulkan origin adalah cara developer "mengakali" larangan itu — dan justru menciptakan lubang besar.
Validasi domain yang lemah
// Rentan: "evil-perusahaan.com" dan "perusahaan.com.jahat.id" ikut lolos
if (origin.includes('perusahaan.com')) { /* izinkan */ }
Pencocokan dengan includes(), startsWith(), atau regex yang tidak diikat di awal-akhir string adalah sumber celah klasik. Gunakan pencocokan persis terhadap daftar putih.
Mengizinkan origin null
Nilai null bisa berasal dari halaman yang di-sandbox atau file lokal, dan mudah dipalsukan penyerang. Jangan pernah memasukkannya ke daftar izin.
Wildcard subdomain
*.perusahaan.com terlihat praktis, tapi berarti satu subdomain kecil yang terbengkalai dan rentan XSS bisa menjadi pintu masuk ke API utama Anda.
Konfigurasi yang Lebih Aman
const daftarIzin = [
'https://app.perusahaan.com',
'https://admin.perusahaan.com'
];
app.use(cors({
origin: (origin, callback) => {
// Izinkan request tanpa origin (mobile app, curl) sesuai kebutuhan
if (!origin) return callback(null, true);
if (daftarIzin.includes(origin)) return callback(null, true);
return callback(new Error('Origin tidak diizinkan'));
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 600
}));
Checklist singkat yang bisa Anda pakai:
- Gunakan daftar putih eksplisit, bukan pemantulan origin.
- Jangan gabungkan wildcard
*dengancredentials: true. - Tambahkan header
Vary: Originagar cache (CDN maupun browser) tidak menyajikan izin milik origin lain. - Batasi method dan header hanya pada yang benar-benar dipakai.
- Pakai
*hanya untuk API publik yang datanya memang terbuka dan tidak butuh cookie. - Tetap terapkan autentikasi dan otorisasi penuh di sisi server.
Hubungannya dengan XSS dan CSRF
Ketiganya sering tertukar, padahal berbeda:
- XSS — penyerang menyisipkan skrip berbahaya ke dalam halaman Anda.
- CSRF — penyerang memancing browser korban mengirim aksi yang tidak diinginkan (mengirim, bukan membaca).
- CORS — aturan yang menentukan apakah JavaScript boleh membaca respons lintas origin.
Yang menarik, XSS mampu melumpuhkan semuanya. Jika penyerang berhasil menjalankan skrip di dalam origin Anda, maka menurut browser ia sudah "sah" berada di dalam — SOP dan CORS tidak lagi menjadi penghalang. Karena itu, mengamankan aplikasi dari XSS tetap menjadi prioritas utama.
Penutup
CORS bukanlah musuh yang harus dimatikan setiap kali muncul error merah di console. Ia adalah pagar pengaman yang bekerja untuk melindungi pengguna Anda.
Godaan terbesar bagi developer pemula adalah menyalakan mode "izinkan semua" agar aplikasi cepat jalan, lalu lupa merapikannya sebelum masuk produksi. Padahal, satu baris konfigurasi yang terlalu longgar bisa membuka jalan bagi kebocoran data pengguna.
Prinsipnya sederhana: pahami dulu kenapa pagar itu ada, baru buka pintunya seperlunya — untuk pihak yang memang Anda percaya.
Post Terkait
CSRF untuk Pemula: Ketika Browser Anda Sendiri Jadi Senjata Penyerang
CSRF memanfaatkan satu kebiasaan browser: mengirim cookie sesi ke domain tujuan tanpa peduli halaman mana yang memicunya...
SQL Injection untuk Pemula: Saat Satu Tanda Kutip Bisa Membobol Database
Kerentanan berumur lebih dari dua dekade ini masih rutin muncul di berita kebocoran data. Pahami kenapa query yang diben...
XSS (Cross-Site Scripting): Cara Kerja Serangan dan Pencegahannya
Bedah tuntas cara kerja Cross-Site Scripting (XSS): tiga jenisnya, pola berpikir penyerang saat mencari celah, dan cara...