Panduan komprehensif untuk meningkatkan keamanan frontend menggunakan Content Security Policy (CSP) dan Cross-Origin Resource Sharing (CORS).
Memperketat Keamanan Frontend: Content Security Policy dan CORS
Dalam lanskap digital yang saling terhubung saat ini, keamanan frontend sangat penting. Aplikasi web semakin menjadi sasaran serangan canggih, menjadikan langkah-langkah keamanan yang kuat menjadi esensial. Dua komponen penting dari arsitektur frontend yang aman adalah Content Security Policy (CSP) dan Cross-Origin Resource Sharing (CORS). Panduan komprehensif ini memberikan tinjauan mendalam tentang teknologi-teknologi ini, menawarkan contoh praktis dan wawasan yang dapat ditindaklanjuti untuk membantu Anda memperkuat aplikasi web Anda terhadap ancaman modern.
Apa itu Content Security Policy (CSP)?
Content Security Policy (CSP) adalah lapisan keamanan tambahan yang membantu mendeteksi dan mengurangi jenis serangan tertentu, termasuk Cross-Site Scripting (XSS) dan serangan injeksi data. CSP diimplementasikan oleh server web yang mengirimkan header respons HTTP Content-Security-Policy ke browser. Header ini mendefinisikan daftar putih sumber yang diizinkan browser untuk memuat sumber daya dari. Dengan membatasi sumber konten yang dapat dimuat oleh browser, CSP membuat penyerang jauh lebih sulit untuk menyuntikkan kode berbahaya ke dalam situs web Anda.
Bagaimana CSP Bekerja
CSP bekerja dengan menginstruksikan browser untuk hanya memuat sumber daya (misalnya, skrip, stylesheet, gambar, font) dari sumber yang disetujui. Sumber-sumber ini ditentukan dalam header CSP menggunakan direktif. Jika browser mencoba memuat sumber daya dari sumber yang tidak diizinkan secara eksplisit, browser akan memblokir permintaan dan melaporkan pelanggaran.
Direktif CSP: Ikhtisar Komprehensif
Direktif CSP mengontrol jenis sumber daya yang dapat dimuat dari sumber tertentu. Berikut adalah rincian beberapa direktif yang paling penting:
- default-src: Menentukan sumber default untuk semua jenis konten. Ini adalah direktif fallback yang berlaku ketika direktif lain yang lebih spesifik tidak ada.
- script-src: Menentukan sumber dari mana skrip dapat dimuat. Ini sangat penting untuk mencegah serangan XSS.
- style-src: Menentukan sumber dari mana stylesheet dapat dimuat.
- img-src: Menentukan sumber dari mana gambar dapat dimuat.
- font-src: Menentukan sumber dari mana font dapat dimuat.
- media-src: Menentukan sumber dari mana audio dan video dapat dimuat.
- object-src: Menentukan sumber dari mana plugin (misalnya, Flash) dapat dimuat. Ini sering diatur ke 'none' untuk menonaktifkan plugin sepenuhnya karena risiko keamanan inheren mereka.
- frame-src: Menentukan sumber dari mana frame (misalnya, <iframe>) dapat dimuat.
- connect-src: Menentukan URL mana yang dapat dihubungi oleh agen pengguna menggunakan antarmuka skrip seperti XMLHttpRequest, WebSocket, dan EventSource.
- base-uri: Menentukan URL yang dapat digunakan dalam elemen <base> dokumen.
- form-action: Menentukan URL tempat pengiriman formulir dapat dikirim.
- upgrade-insecure-requests: Menginstruksikan agen pengguna untuk secara otomatis meningkatkan permintaan yang tidak aman (HTTP) menjadi permintaan yang aman (HTTPS).
- report-uri: Menentukan URL tempat browser harus mengirim laporan tentang pelanggaran CSP. Direktif ini tidak digunakan lagi dan digantikan oleh `report-to`.
- report-to: Menentukan nama grup pelaporan yang didefinisikan dalam header `Report-To`, tempat browser harus mengirim laporan tentang pelanggaran CSP.
Kata Kunci Daftar Sumber CSP
Dalam direktif CSP, Anda dapat menggunakan kata kunci daftar sumber untuk menentukan sumber yang diizinkan. Berikut adalah beberapa kata kunci umum:
- 'self': Mengizinkan sumber daya dari asal yang sama (skema dan host) dengan dokumen.
- 'none': Melarang sumber daya dari semua sumber.
- 'unsafe-inline': Mengizinkan penggunaan skrip dan gaya inline (misalnya, tag <script> dan atribut style). Gunakan dengan sangat hati-hati karena secara signifikan melemahkan perlindungan CSP terhadap XSS.
- 'unsafe-eval': Mengizinkan penggunaan fungsi evaluasi kode dinamis seperti
eval()danFunction(). Gunakan dengan sangat hati-hati karena memperkenalkan risiko keamanan yang signifikan. - 'unsafe-hashes': Mengizinkan handler peristiwa inline tertentu atau tag <style> yang cocok dengan hash yang ditentukan. Memerlukan dukungan browser. Gunakan dengan hati-hati.
- 'strict-dynamic': Menentukan bahwa kepercayaan yang diberikan secara eksplisit kepada skrip yang ada dalam markup, dengan menyertainya dengan nonce atau hash, harus disebarluaskan ke semua skrip yang dimuat oleh skrip root tersebut.
- data: Mengizinkan data: URI (misalnya, gambar inline yang dikodekan sebagai base64). Gunakan dengan hati-hati.
- https:: Mengizinkan sumber daya untuk dimuat melalui HTTPS dari domain mana pun.
- [hostname]: Mengizinkan sumber daya dari domain tertentu (misalnya, example.com). Anda juga dapat menentukan nomor port (misalnya, example.com:8080).
- [scheme]://[hostname]:[port]: URI yang sepenuhnya memenuhi syarat, mengizinkan sumber daya dari skema, host, dan port yang ditentukan.
Contoh CSP Praktis
Mari kita lihat beberapa contoh praktis header CSP:
Contoh 1: CSP Dasar dengan 'self'
Kebijakan ini hanya mengizinkan sumber daya dari asal yang sama:
Content-Security-Policy: default-src 'self'
Contoh 2: Mengizinkan Skrip dari Domain Tertentu
Kebijakan ini mengizinkan skrip dari domain Anda sendiri dan CDN tepercaya:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Contoh 3: Menonaktifkan Skrip dan Gaya Inline
Kebijakan ini melarang skrip dan gaya inline, yang merupakan pertahanan yang kuat terhadap XSS:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'
Penting: Menonaktifkan skrip inline mengharuskan Anda merefaktor HTML Anda untuk memindahkan skrip inline ke file eksternal.
Contoh 4: Menggunakan Nonce untuk Skrip Inline
Jika Anda harus menggunakan skrip inline, gunakan nonce (token sekali pakai, acak secara kriptografis) untuk memasukkan daftar putih blok skrip inline tertentu. Ini lebih aman daripada 'unsafe-inline'. Server harus menghasilkan nonce unik untuk setiap permintaan dan menyertakannya dalam header CSP dan tag <script>.
Content-Security-Policy: default-src 'self'; script-src 'nonce-r4nd0mN0nc3'; style-src 'self'
<script nonce="r4nd0mN0nc3"> console.log('Inline script'); </script>
Catatan: Ingatlah untuk menghasilkan nonce baru untuk setiap permintaan. Jangan gunakan kembali nonce!
Contoh 5: Menggunakan Hash untuk Gaya Inline
Mirip dengan nonce, hash dapat digunakan untuk memasukkan daftar putih blok <style> inline tertentu. Ini dilakukan dengan menghasilkan hash SHA256, SHA384, atau SHA512 dari konten gaya.
Content-Security-Policy: default-src 'self'; style-src 'sha256-HASHEDSTYLES'
<style sha256="HASHEDSTYLES"> body { background-color: #f0f0f0; } </style>
Catatan: Hash kurang fleksibel daripada nonce karena setiap perubahan pada konten gaya akan membatalkan hash.
Contoh 6: Melaporkan Pelanggaran CSP
Untuk memantau pelanggaran CSP, gunakan direktif report-uri atau report-to:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint;
Anda juga perlu mengonfigurasi header Report-To. Header Report-To mendefinisikan satu atau lebih grup pelaporan, yang menentukan di mana dan bagaimana laporan harus dikirim.
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Menguji dan Menyebarkan CSP
Menerapkan CSP memerlukan perencanaan dan pengujian yang cermat. Mulailah dengan kebijakan yang ketat dan secara bertahap longgarkan sesuai kebutuhan. Gunakan header Content-Security-Policy-Report-Only untuk menguji kebijakan Anda tanpa memblokir sumber daya. Header ini melaporkan pelanggaran tanpa memberlakukan kebijakan, memungkinkan Anda untuk mengidentifikasi dan memperbaiki masalah sebelum menyebarkan kebijakan dalam produksi.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint;
Analisis laporan yang dihasilkan oleh browser untuk mengidentifikasi pelanggaran apa pun dan sesuaikan kebijakan Anda sesuai dengan itu. Setelah Anda yakin bahwa kebijakan Anda berfungsi dengan benar, sebarkan menggunakan header Content-Security-Policy.
Praktik Terbaik untuk CSP
- Mulailah dengan default-src: Selalu definisikan
default-srcuntuk menetapkan kebijakan dasar. - Bersikap spesifik: Gunakan direktif dan kata kunci daftar sumber tertentu untuk membatasi cakupan kebijakan Anda.
- Hindari 'unsafe-inline' dan 'unsafe-eval': Kata kunci ini secara signifikan melemahkan CSP dan harus dihindari sebisa mungkin.
- Gunakan nonce atau hash untuk skrip dan gaya inline: Jika Anda harus menggunakan skrip atau gaya inline, gunakan nonce atau hash untuk memasukkan daftar putih blok kode tertentu.
- Pantau pelanggaran CSP: Gunakan direktif
report-uriataureport-tountuk memantau pelanggaran CSP dan sesuaikan kebijakan Anda sesuai dengan itu. - Uji secara menyeluruh: Gunakan header
Content-Security-Policy-Report-Onlyuntuk menguji kebijakan Anda sebelum menyebarkannya dalam produksi. - Ulangi dan sempurnakan: CSP bukanlah konfigurasi satu kali. Terus pantau dan sempurnakan kebijakan Anda untuk beradaptasi dengan perubahan dalam aplikasi Anda dan lanskap ancaman.
Apa itu Cross-Origin Resource Sharing (CORS)?
Cross-Origin Resource Sharing (CORS) adalah mekanisme yang memungkinkan halaman web dari satu asal (domain) untuk mengakses sumber daya dari asal yang berbeda. Secara default, browser memberlakukan Kebijakan Same-Origin, yang mencegah skrip membuat permintaan ke asal yang berbeda dari asal tempat skrip berasal. CORS menyediakan cara untuk secara selektif melonggarkan batasan ini, memungkinkan permintaan lintas asal yang sah sambil melindungi terhadap serangan jahat.
Memahami Kebijakan Same-Origin
Kebijakan Same-Origin adalah mekanisme keamanan fundamental yang mencegah skrip jahat dari satu situs web mengakses data sensitif di situs web lain. Asal didefinisikan oleh skema (protokol), host (domain), dan port. Dua URL memiliki asal yang sama jika dan hanya jika mereka memiliki skema, host, dan port yang sama.
Contoh:
https://www.example.com/app1/index.htmldanhttps://www.example.com/app2/index.htmlmemiliki asal yang sama.https://www.example.com/index.htmldanhttp://www.example.com/index.htmlmemiliki asal yang berbeda (skema yang berbeda).https://www.example.com/index.htmldanhttps://sub.example.com/index.htmlmemiliki asal yang berbeda (host yang berbeda).https://www.example.com:8080/index.htmldanhttps://www.example.com:80/index.htmlmemiliki asal yang berbeda (port yang berbeda).
Bagaimana CORS Bekerja
Ketika halaman web membuat permintaan lintas asal, browser pertama-tama mengirimkan permintaan "preflight" ke server. Permintaan preflight menggunakan metode HTTP OPTIONS dan menyertakan header yang menunjukkan metode HTTP dan header yang akan digunakan oleh permintaan sebenarnya. Server kemudian merespons dengan header yang menunjukkan apakah permintaan lintas asal diizinkan.
Jika server mengizinkan permintaan, server menyertakan header Access-Control-Allow-Origin dalam respons. Header ini menentukan asal yang diizinkan untuk mengakses sumber daya. Browser kemudian melanjutkan dengan permintaan sebenarnya. Jika server tidak mengizinkan permintaan, server tidak menyertakan header Access-Control-Allow-Origin, dan browser memblokir permintaan.
Header CORS: Tinjauan Mendalam
CORS bergantung pada header HTTP untuk berkomunikasi antara browser dan server. Berikut adalah header CORS utama:
- Access-Control-Allow-Origin: Menentukan asal yang diizinkan untuk mengakses sumber daya. Header ini dapat berisi asal tertentu (misalnya,
https://www.example.com), wildcard (*), ataunull. Menggunakan*memungkinkan permintaan dari asal mana pun, yang umumnya tidak direkomendasikan karena alasan keamanan. Menggunakan `null` hanya sesuai untuk "respons buram" seperti ketika sumber daya diambil menggunakan protokol `file://` atau URI data. - Access-Control-Allow-Methods: Menentukan metode HTTP yang diizinkan untuk permintaan lintas asal (misalnya,
GET, POST, PUT, DELETE). - Access-Control-Allow-Headers: Menentukan header HTTP yang diizinkan dalam permintaan lintas asal. Ini penting untuk menangani header khusus.
- Access-Control-Allow-Credentials: Menunjukkan apakah browser harus menyertakan kredensial (misalnya, cookie, header otorisasi) dalam permintaan lintas asal. Header ini harus diatur ke
trueuntuk mengizinkan kredensial. - Access-Control-Expose-Headers: Menentukan header mana yang dapat diekspos ke klien. Secara default, hanya sejumlah header terbatas yang diekspos.
- Access-Control-Max-Age: Menentukan jumlah waktu maksimum (dalam detik) browser dapat menyimpan cache permintaan preflight.
- Origin: Ini adalah header permintaan yang dikirim oleh browser untuk menunjukkan asal permintaan.
- Vary: Header HTTP umum, tetapi penting untuk CORS. Ketika `Access-Control-Allow-Origin` dihasilkan secara dinamis, header `Vary: Origin` harus disertakan dalam respons untuk menginstruksikan mekanisme caching bahwa respons bervariasi berdasarkan header permintaan `Origin`.
Contoh CORS Praktis
Mari kita lihat beberapa contoh praktis konfigurasi CORS:
Contoh 1: Mengizinkan Permintaan dari Asal Tertentu
Konfigurasi ini hanya mengizinkan permintaan dari https://www.example.com:
Access-Control-Allow-Origin: https://www.example.com
Contoh 2: Mengizinkan Permintaan dari Asal Mana Pun (Tidak Direkomendasikan)
Konfigurasi ini mengizinkan permintaan dari asal mana pun. Gunakan dengan hati-hati karena dapat menimbulkan risiko keamanan:
Access-Control-Allow-Origin: *
Contoh 3: Mengizinkan Metode dan Header Tertentu
Konfigurasi ini mengizinkan metode GET, POST, dan PUT, dan header Content-Type dan Authorization:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Contoh 4: Mengizinkan Kredensial
Untuk mengizinkan kredensial (misalnya, cookie), Anda perlu mengatur Access-Control-Allow-Credentials ke true dan menentukan asal tertentu (Anda tidak dapat menggunakan * saat mengizinkan kredensial):
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
Anda juga perlu mengatur credentials: 'include' dalam permintaan JavaScript fetch/XMLHttpRequest Anda.
fetch('https://api.example.com/data', {
credentials: 'include'
})
Permintaan Preflight CORS
Untuk jenis permintaan lintas asal tertentu (misalnya, permintaan dengan header khusus atau metode selain GET, HEAD, atau POST dengan Content-Type dari application/x-www-form-urlencoded, multipart/form-data, atau text/plain), browser mengirimkan permintaan preflight menggunakan metode OPTIONS. Server harus merespons permintaan preflight dengan header CORS yang sesuai untuk menunjukkan apakah permintaan sebenarnya diizinkan.
Berikut adalah contoh permintaan dan respons preflight:
Permintaan Preflight (OPTIONS):
OPTIONS /data HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Respons Preflight (200 OK):
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Header Access-Control-Max-Age menentukan berapa lama browser dapat menyimpan cache respons preflight, mengurangi jumlah permintaan preflight.
CORS dan JSONP
JSON with Padding (JSONP) adalah teknik lama untuk melewati Kebijakan Same-Origin. Namun, JSONP memiliki risiko keamanan yang signifikan dan harus dihindari demi CORS. JSONP bergantung pada penyuntikan tag <script> ke dalam halaman, yang dapat menjalankan kode arbitrer. CORS menyediakan cara yang lebih aman dan fleksibel untuk menangani permintaan lintas asal.
Praktik Terbaik untuk CORS
- Hindari menggunakan *: Hindari menggunakan wildcard (*) di header
Access-Control-Allow-Origin, karena ini memungkinkan permintaan dari asal mana pun. Sebagai gantinya, tentukan asal tertentu yang diizinkan untuk mengakses sumber daya. - Bersikap spesifik dengan metode dan header: Tentukan metode dan header HTTP yang tepat yang diizinkan dalam header
Access-Control-Allow-MethodsdanAccess-Control-Allow-Headers. - Gunakan Access-Control-Allow-Credentials dengan hati-hati: Hanya aktifkan
Access-Control-Allow-Credentialsjika Anda perlu mengizinkan kredensial (misalnya, cookie) dalam permintaan lintas asal. Waspadai implikasi keamanan dari mengizinkan kredensial. - Amankan permintaan preflight Anda: Pastikan bahwa server Anda menangani permintaan preflight dengan benar dan mengembalikan header CORS yang benar.
- Gunakan HTTPS: Selalu gunakan HTTPS untuk asal dan sumber daya yang Anda akses lintas asal. Ini membantu melindungi terhadap serangan man-in-the-middle.
- Vary: Origin: Jika Anda menghasilkan header `Access-Control-Allow-Origin` secara dinamis, selalu sertakan header `Vary: Origin` untuk mencegah masalah caching.
CSP dan CORS dalam Praktik: Pendekatan Gabungan
Meskipun CSP dan CORS keduanya menangani masalah keamanan, mereka beroperasi pada lapisan yang berbeda dan memberikan perlindungan pelengkap. CSP berfokus pada mencegah browser memuat konten berbahaya, sementara CORS berfokus pada mengendalikan asal mana yang dapat mengakses sumber daya di server Anda.
Dengan menggabungkan CSP dan CORS, Anda dapat membuat postur keamanan yang lebih kuat untuk aplikasi web Anda. Misalnya, Anda dapat menggunakan CSP untuk membatasi sumber dari mana skrip dapat dimuat, dan CORS untuk mengontrol asal mana yang dapat mengakses titik akhir API Anda.
Contoh: Mengamankan API dengan CSP dan CORS
Katakanlah Anda memiliki API yang dihosting di https://api.example.com yang ingin Anda dapatkan diakses hanya dari https://www.example.com. Anda dapat mengonfigurasi server Anda untuk mengembalikan header berikut:
Header Respons API (https://api.example.com):
Access-Control-Allow-Origin: https://www.example.com
Content-Type: application/json
Dan Anda dapat mengonfigurasi situs web Anda (https://www.example.com) untuk menggunakan header CSP berikut:
Header CSP Situs Web (https://www.example.com):
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com;
Kebijakan CSP ini memungkinkan situs web untuk memuat skrip dan terhubung ke API, tetapi mencegahnya memuat skrip atau terhubung ke domain lain.
Kesimpulan
Content Security Policy (CSP) dan Cross-Origin Resource Sharing (CORS) adalah alat penting untuk memperketat keamanan aplikasi frontend Anda. Dengan mengonfigurasi CSP dan CORS dengan hati-hati, Anda dapat secara signifikan mengurangi risiko serangan XSS, serangan injeksi data, dan kerentanan keamanan lainnya. Ingatlah untuk memulai dengan kebijakan yang ketat, menguji secara menyeluruh, dan terus memantau dan menyempurnakan konfigurasi Anda untuk beradaptasi dengan perubahan dalam aplikasi Anda dan lanskap ancaman yang berkembang. Dengan memprioritaskan keamanan frontend, Anda dapat melindungi pengguna Anda dan memastikan integritas aplikasi web Anda di dunia digital yang semakin kompleks saat ini.