Eksplorasi mendalam tentang Cross-Origin Resource Sharing (CORS) dan preflight request. Pelajari cara menangani masalah CORS dan amankan aplikasi web Anda untuk audiens global.
Membongkar Misteri CORS: Kupas Tuntas Penanganan Preflight Request pada JavaScript
Di dunia pengembangan web yang terus berkembang, keamanan adalah yang utama. Cross-Origin Resource Sharing (CORS) adalah mekanisme keamanan penting yang diterapkan oleh peramban web untuk membatasi halaman web membuat permintaan ke domain yang berbeda dari domain yang menyajikan halaman web tersebut. Ini adalah fitur keamanan fundamental yang dirancang untuk mencegah situs web berbahaya mengakses data sensitif. Panduan komprehensif ini akan membahas seluk-beluk CORS, dengan fokus khusus pada penanganan preflight request. Kita akan menjelajahi 'mengapa,' 'apa,' dan 'bagaimana' tentang CORS, memberikan contoh praktis dan solusi untuk masalah umum yang dihadapi oleh para pengembang di seluruh dunia.
Memahami Same-Origin Policy
Inti dari CORS adalah Same-Origin Policy (SOP). Kebijakan ini adalah mekanisme keamanan tingkat peramban yang membatasi skrip yang berjalan di satu origin untuk mengakses sumber daya dari origin yang berbeda. Sebuah origin didefinisikan oleh protokol (misalnya, HTTP atau HTTPS), domain (misalnya, example.com), dan port (misalnya, 80 atau 443). Dua URL memiliki origin yang sama jika ketiga komponen ini cocok persis.
Sebagai contoh:
https://www.example.com/app1/index.html
danhttps://www.example.com/app2/index.html
memiliki origin yang sama (protokol, domain, dan port yang sama).https://www.example.com/index.html
danhttp://www.example.com/index.html
memiliki origin yang berbeda (protokol berbeda).https://www.example.com/index.html
danhttps://api.example.com/index.html
memiliki origin yang berbeda (subdomain yang berbeda dianggap domain yang berbeda).https://www.example.com:8080/index.html
danhttps://www.example.com/index.html
memiliki origin yang berbeda (port berbeda).
SOP dirancang untuk mencegah skrip berbahaya di satu situs web mengakses data sensitif, seperti cookie atau informasi autentikasi pengguna, di situs web lain. Meskipun penting untuk keamanan, SOP juga bisa bersifat membatasi, terutama ketika permintaan lintas-origin yang sah diperlukan.
Apa itu Cross-Origin Resource Sharing (CORS)?
CORS adalah mekanisme yang memungkinkan server untuk menentukan origin mana (domain, skema, atau port) yang diizinkan untuk mengakses sumber daya mereka. Ini pada dasarnya melonggarkan SOP, memungkinkan akses lintas-origin yang terkontrol. CORS diimplementasikan menggunakan header HTTP yang dipertukarkan antara klien (biasanya peramban web) dan server.
Ketika peramban membuat permintaan lintas-origin (yaitu, permintaan ke origin yang berbeda dari halaman saat ini), ia pertama-tama memeriksa apakah server mengizinkan permintaan tersebut. Ini dilakukan dengan memeriksa header Access-Control-Allow-Origin
dalam respons server. Jika origin permintaan tercantum dalam header ini (atau jika header diatur ke *
, yang mengizinkan semua origin), peramban mengizinkan permintaan untuk dilanjutkan. Jika tidak, peramban memblokir permintaan, mencegah kode JavaScript mengakses data respons.
Peran Preflight Request
Untuk jenis permintaan lintas-origin tertentu, peramban memulai sebuah preflight request. Ini adalah permintaan OPTIONS
yang dikirim ke server sebelum permintaan yang sebenarnya. Tujuan dari preflight request adalah untuk menentukan apakah server bersedia menerima permintaan yang sebenarnya. Server merespons preflight request dengan informasi tentang metode, header, dan batasan lain yang diizinkan.
Preflight request dipicu ketika permintaan lintas-origin memenuhi salah satu dari kondisi berikut:
- Metode permintaan bukan
GET
,HEAD
, atauPOST
. - Permintaan menyertakan header kustom (yaitu, header selain yang ditambahkan secara otomatis oleh peramban).
- Header
Content-Type
diatur ke apa pun selainapplication/x-www-form-urlencoded
,multipart/form-data
, atautext/plain
. - Permintaan menggunakan objek
ReadableStream
di dalam body.
Sebagai contoh, permintaan PUT
dengan Content-Type
application/json
akan memicu preflight request karena menggunakan metode yang berbeda dari yang diizinkan dan tipe konten yang berpotensi tidak diizinkan.
Mengapa Perlu Preflight Request?
Preflight request sangat penting untuk keamanan karena memberikan server kesempatan untuk menolak permintaan lintas-origin yang berpotensi berbahaya sebelum dieksekusi. Tanpa preflight request, situs web berbahaya berpotensi mengirim permintaan sewenang-wenang ke server tanpa persetujuan eksplisit dari server. Preflight request memungkinkan server untuk memvalidasi bahwa permintaan tersebut dapat diterima dan mencegah operasi yang berpotensi berbahaya.
Menangani Preflight Request di Sisi Server
Menangani preflight request dengan benar sangat penting untuk memastikan bahwa aplikasi web Anda berfungsi dengan benar dan aman. Server harus merespons permintaan OPTIONS
dengan header CORS yang sesuai untuk menunjukkan apakah permintaan yang sebenarnya diizinkan.
Berikut adalah rincian header CORS utama yang digunakan dalam respons preflight:
Access-Control-Allow-Origin
: Header ini menentukan origin mana yang diizinkan untuk mengakses sumber daya. Ini dapat diatur ke origin tertentu (misalnya,https://www.example.com
) atau ke*
untuk mengizinkan semua origin. Namun, menggunakan*
umumnya tidak disarankan karena alasan keamanan, terutama jika server menangani data sensitif.Access-Control-Allow-Methods
: Header ini menentukan metode HTTP yang diizinkan untuk permintaan lintas-origin (misalnya,GET
,POST
,PUT
,DELETE
).Access-Control-Allow-Headers
: Header ini menentukan daftar header HTTP non-standar yang diizinkan dalam permintaan yang sebenarnya. Ini diperlukan jika klien mengirim header kustom, sepertiX-Custom-Header
atauAuthorization
.Access-Control-Allow-Credentials
: Header ini menunjukkan apakah permintaan yang sebenarnya dapat menyertakan kredensial, seperti cookie atau header otorisasi. Ini harus diatur ketrue
jika kode sisi klien mengirimkan kredensial dan server harus menerimanya. Catatan: ketika header ini diatur ke `true`, `Access-Control-Allow-Origin` *tidak bisa* diatur ke `*`. Anda harus menentukan origin.Access-Control-Max-Age
: Header ini menentukan jumlah waktu maksimum (dalam detik) peramban dapat menyimpan cache respons preflight. Ini dapat membantu meningkatkan kinerja dengan mengurangi jumlah preflight request yang dikirim.
Contoh: Menangani Preflight Request di Node.js dengan Express
Berikut adalah contoh cara menangani preflight request di aplikasi Node.js menggunakan kerangka kerja Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Aktifkan CORS untuk semua origin (hanya untuk tujuan pengembangan!)
// Di lingkungan produksi, tentukan origin yang diizinkan untuk keamanan yang lebih baik.
app.use(cors()); //atau app.use(cors({origin: 'https://www.example.com'}));
// Rute untuk menangani permintaan OPTIONS (preflight)
app.options('/data', cors()); // Aktifkan CORS untuk satu rute. Atau tentukan origin: cors({origin: 'https://www.example.com'})
// Rute untuk menangani permintaan GET
app.get('/data', (req, res) => {
res.json({ message: 'Ini adalah data lintas-origin!' });
});
// Rute untuk menangani preflight dan permintaan post
app.options('/resource', cors()); // aktifkan permintaan pre-flight untuk permintaan DELETE
app.delete('/resource', cors(), (req, res, next) => {
res.send('hapus sumber daya')
})
const port = 3000;
app.listen(port, () => {
console.log(`Server berjalan di port ${port}`);
});
Dalam contoh ini, kita menggunakan middleware cors
untuk menangani permintaan CORS. Untuk kontrol yang lebih terperinci, CORS dapat diaktifkan per-rute. Catatan: di lingkungan produksi, sangat disarankan untuk menentukan origin yang diizinkan menggunakan opsi origin
daripada mengizinkan semua origin. Mengizinkan semua origin menggunakan *
dapat mengekspos aplikasi Anda terhadap kerentanan keamanan.
Contoh: Menangani Preflight Request di Python dengan Flask
Berikut adalah contoh cara menangani preflight request di aplikasi Python menggunakan kerangka kerja Flask dan ekstensi flask_cors
:
from flask import Flask, jsonify
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app) # Aktifkan CORS untuk semua rute
@app.route('/data')
@cross_origin()
def get_data():
data = {"message": "Ini adalah data lintas-origin!"}
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
Ini adalah penggunaan yang paling sederhana. Seperti sebelumnya, origin dapat dibatasi. Lihat dokumentasi flask-cors untuk detailnya.
Contoh: Menangani Preflight Request di Java dengan Spring Boot
Berikut adalah contoh cara menangani preflight request di aplikasi Java menggunakan Spring Boot:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class CorsApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/data").allowedOrigins("http://localhost:8080");
}
};
}
}
Dan controller yang sesuai:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DataController {
@GetMapping("/data")
public String getData() {
return "Ini adalah data lintas-origin!";
}
}
Masalah Umum CORS dan Solusinya
Meskipun penting, CORS sering kali dapat menjadi sumber frustrasi bagi pengembang. Berikut adalah beberapa masalah umum CORS dan solusinya:
-
Kesalahan: "No 'Access-Control-Allow-Origin' header is present on the requested resource."
Kesalahan ini menunjukkan bahwa server tidak mengembalikan header
Access-Control-Allow-Origin
dalam responsnya. Untuk memperbaikinya, pastikan server dikonfigurasi untuk menyertakan header tersebut dan diatur ke origin yang benar atau ke*
(jika sesuai).Solusi: Konfigurasikan server untuk menyertakan header `Access-Control-Allow-Origin` dalam responsnya, atur ke origin situs web yang meminta atau ke `*` untuk mengizinkan semua origin (gunakan dengan hati-hati).
-
Kesalahan: "Response to preflight request doesn't pass access control check: Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response."
Kesalahan ini menunjukkan bahwa server tidak mengizinkan header kustom (
X-Custom-Header
dalam contoh ini) dalam permintaan lintas-origin. Untuk memperbaikinya, pastikan server menyertakan header tersebut dalam headerAccess-Control-Allow-Headers
pada respons preflight.Solusi: Tambahkan header kustom (misalnya, `X-Custom-Header`) ke header `Access-Control-Allow-Headers` dalam respons preflight server.
-
Kesalahan: "Credentials flag is 'true', but the 'Access-Control-Allow-Origin' header is '*'."
Ketika header
Access-Control-Allow-Credentials
diatur ketrue
, headerAccess-Control-Allow-Origin
harus diatur ke origin tertentu, bukan*
. Ini karena mengizinkan kredensial dari semua origin akan menjadi risiko keamanan.Solusi: Saat menggunakan kredensial, atur `Access-Control-Allow-Origin` ke origin tertentu, bukan `*`.
-
Preflight request tidak dikirim.
Periksa kembali bahwa kode Javascript Anda menyertakan properti
credentials: 'include'
. Periksa juga bahwa server Anda mengizinkanAccess-Control-Allow-Credentials: true
. -
Konfigurasi yang bertentangan antara server dan klien.
Periksa dengan cermat konfigurasi CORS sisi server Anda bersamaan dengan pengaturan sisi klien. Ketidakcocokan (misalnya, server hanya mengizinkan permintaan GET tetapi klien mengirim POST) akan menyebabkan kesalahan CORS.
CORS dan Praktik Terbaik Keamanan
Meskipun CORS memungkinkan akses lintas-origin yang terkontrol, penting untuk mengikuti praktik terbaik keamanan untuk mencegah kerentanan:
- Hindari menggunakan
*
di headerAccess-Control-Allow-Origin
di lingkungan produksi. Ini mengizinkan semua origin untuk mengakses sumber daya Anda, yang bisa menjadi risiko keamanan. Sebaliknya, tentukan origin yang diizinkan secara spesifik. - Pertimbangkan dengan cermat metode dan header mana yang akan diizinkan. Hanya izinkan metode dan header yang benar-benar diperlukan agar aplikasi Anda berfungsi dengan benar.
- Terapkan mekanisme autentikasi dan otorisasi yang tepat. CORS bukanlah pengganti untuk autentikasi dan otorisasi. Pastikan API Anda dilindungi oleh langkah-langkah keamanan yang sesuai.
- Validasi dan sanitasi semua input pengguna. Ini membantu mencegah serangan cross-site scripting (XSS) dan kerentanan lainnya.
- Selalu perbarui konfigurasi CORS sisi server Anda. Tinjau dan perbarui secara berkala konfigurasi CORS Anda untuk memastikan selaras dengan persyaratan keamanan aplikasi Anda.
CORS di Berbagai Lingkungan Pengembangan
Masalah CORS dapat bermanifestasi secara berbeda di berbagai lingkungan dan teknologi pengembangan. Berikut adalah cara pendekatan CORS dalam beberapa skenario umum:
Lingkungan Pengembangan Lokal
Selama pengembangan lokal, masalah CORS bisa sangat mengganggu. Peramban sering memblokir permintaan dari server pengembangan lokal Anda (misalnya, localhost:3000
) ke API jarak jauh. Beberapa teknik dapat meringankan masalah ini:
- Ekstensi Peramban: Ekstensi seperti "Allow CORS: Access-Control-Allow-Origin" dapat menonaktifkan sementara pembatasan CORS untuk tujuan pengujian. Namun, *jangan pernah* gunakan ini di lingkungan produksi.
- Server Proksi: Konfigurasikan server proksi yang meneruskan permintaan dari server pengembangan lokal Anda ke API jarak jauh. Ini secara efektif membuat permintaan menjadi "same-origin" dari perspektif peramban. Alat seperti
http-proxy-middleware
(untuk Node.js) sangat membantu untuk ini. - Konfigurasi CORS Server: Bahkan selama pengembangan, praktik terbaik adalah mengonfigurasi server API Anda untuk secara eksplisit mengizinkan permintaan dari origin pengembangan lokal Anda (misalnya,
http://localhost:3000
). Ini mensimulasikan konfigurasi CORS dunia nyata dan membantu Anda menemukan masalah lebih awal.
Lingkungan Serverless (misalnya, AWS Lambda, Google Cloud Functions)
Fungsi serverless sering memerlukan konfigurasi CORS yang cermat. Banyak platform serverless menyediakan dukungan CORS bawaan, tetapi sangat penting untuk mengonfigurasinya dengan benar:
- Pengaturan Spesifik Platform: Gunakan opsi konfigurasi CORS bawaan platform. AWS Lambda, misalnya, memungkinkan Anda menentukan origin, metode, dan header yang diizinkan langsung di pengaturan API Gateway.
- Middleware/Pustaka: Untuk fleksibilitas yang lebih besar, Anda dapat menggunakan middleware atau pustaka untuk menangani CORS di dalam kode fungsi serverless Anda. Ini mirip dengan pendekatan yang digunakan di lingkungan server tradisional (misalnya, menggunakan paket `cors` dalam fungsi Node.js Lambda).
- Pertimbangkan Metode
OPTIONS
: Pastikan fungsi serverless Anda menangani permintaanOPTIONS
dengan benar. Ini sering kali melibatkan pembuatan rute terpisah yang mengembalikan header CORS yang sesuai.
Pengembangan Aplikasi Mobile (misalnya, React Native, Flutter)
CORS bukanlah perhatian langsung untuk aplikasi mobile asli (Android, iOS), karena mereka biasanya tidak memberlakukan kebijakan same-origin dengan cara yang sama seperti peramban web. Namun, CORS masih bisa relevan jika aplikasi mobile Anda menggunakan web view untuk menampilkan konten web atau jika Anda menggunakan kerangka kerja seperti React Native atau Flutter yang memanfaatkan JavaScript:
- Web View: Jika aplikasi mobile Anda menggunakan web view untuk menampilkan konten web, aturan CORS yang sama berlaku seperti di peramban web. Konfigurasikan server Anda untuk mengizinkan permintaan dari origin konten web.
- React Native/Flutter: Kerangka kerja ini menggunakan JavaScript untuk membuat permintaan API. Meskipun lingkungan asli mungkin tidak memberlakukan CORS secara langsung, klien HTTP yang mendasarinya (misalnya,
fetch
) mungkin masih menunjukkan perilaku seperti CORS dalam situasi tertentu. - Klien HTTP Asli: Saat membuat permintaan API langsung dari kode asli (misalnya, menggunakan OkHttp di Android atau URLSession di iOS), CORS umumnya bukan faktor. Namun, Anda masih perlu mempertimbangkan praktik terbaik keamanan seperti autentikasi dan otorisasi yang tepat.
Pertimbangan Global untuk Konfigurasi CORS
Saat mengonfigurasi CORS untuk aplikasi yang dapat diakses secara global, sangat penting untuk mempertimbangkan faktor-faktor seperti:
- Kedaulatan Data: Peraturan di beberapa wilayah mengharuskan data berada di dalam wilayah tersebut. CORS mungkin terlibat saat mengakses sumber daya lintas batas, berpotensi melanggar hukum residensi data.
- Kebijakan Keamanan Regional: Negara yang berbeda mungkin memiliki peraturan dan pedoman keamanan siber yang berbeda yang memengaruhi bagaimana CORS harus diterapkan dan diamankan.
- Content Delivery Networks (CDNs): Pastikan CDN Anda dikonfigurasi dengan benar untuk meneruskan header CORS yang diperlukan. CDN yang tidak dikonfigurasi dengan benar dapat menghapus header CORS, yang menyebabkan kesalahan tak terduga.
- Load Balancers dan Proxy: Verifikasi bahwa setiap load balancer atau reverse proxy di infrastruktur Anda menangani preflight request dengan benar dan meneruskan header CORS.
- Dukungan Multibahasa: Pertimbangkan bagaimana CORS berinteraksi dengan strategi internasionalisasi (i18n) dan lokalisasi (l10n) aplikasi Anda. Pastikan kebijakan CORS konsisten di berbagai versi bahasa aplikasi Anda.
Pengujian dan Debugging CORS
Menguji dan melakukan debug CORS secara efektif sangat penting. Berikut adalah beberapa teknik:
- Alat Pengembang Peramban: Konsol pengembang peramban adalah perhentian pertama Anda. Tab "Network" akan menampilkan preflight request dan responsnya, mengungkapkan apakah header CORS ada dan dikonfigurasi dengan benar.
- Alat Baris Perintah `curl`: Gunakan `curl -v -X OPTIONS
` untuk mengirim preflight request secara manual dan memeriksa header respons server. - Pemeriksa CORS Online: Banyak alat online dapat membantu memvalidasi konfigurasi CORS Anda. Cukup cari "CORS checker."
- Tes Unit dan Integrasi: Tulis tes otomatis untuk memverifikasi bahwa konfigurasi CORS Anda berfungsi seperti yang diharapkan. Tes ini harus mencakup permintaan lintas-origin yang berhasil dan skenario di mana CORS harus memblokir akses.
- Pencatatan dan Pemantauan: Terapkan pencatatan untuk melacak peristiwa terkait CORS, seperti preflight request dan permintaan yang diblokir. Pantau log Anda untuk aktivitas mencurigakan atau kesalahan konfigurasi.
Kesimpulan
Cross-Origin Resource Sharing (CORS) adalah mekanisme keamanan vital yang memungkinkan akses lintas-origin terkontrol ke sumber daya web. Memahami cara kerja CORS, terutama preflight request, sangat penting untuk membangun aplikasi web yang aman dan andal. Dengan mengikuti praktik terbaik yang diuraikan dalam panduan ini, Anda dapat secara efektif menangani masalah CORS dan melindungi aplikasi Anda dari potensi kerentanan. Ingatlah untuk selalu memprioritaskan keamanan dan mempertimbangkan dengan cermat implikasi dari konfigurasi CORS Anda.
Seiring berkembangnya pengembangan web, CORS akan terus menjadi aspek penting dari keamanan web. Tetap terinformasi tentang praktik dan teknik terbaik CORS terbaru sangat penting untuk membangun aplikasi web yang aman dan dapat diakses secara global.