Nonce CSP Mengikat Skrip Inline ke Respons Individual
JavaScript inline menimbulkan batas yang sulit bagi Content Security Policy yang ketat. Policy yang mengizinkan seluruh skrip inline melalui 'unsafe-inline' memberi blok skrip hasil injeksi hak eksekusi yang sama dengan kode yang memang dimaksudkan aplikasi. Nonce menyediakan mekanisme yang lebih sempit: server membuat nilai yang sulit diprediksi untuk satu respons, menaruh nilai tersebut di policy, lalu memasangnya hanya pada elemen skrip yang memang boleh berjalan.
Browser kemudian membandingkan dua sisi. Elemen skrip yang membawa nonce milik respons dapat memenuhi sumber nonce pada script-src; skrip inline hasil injeksi yang tidak memiliki nilai tersebut tidak memperoleh izin hanya karena muncul di dokumen.
Kontrol ini mengatur eksekusi, bukan sanitasi input. Aplikasi tetap perlu menangani data tidak tepercaya secara aman, dan nonce tidak boleh berubah menjadi atribut markup yang dapat dikendalikan penyerang.
Nonce berlaku untuk satu respons
Nonce seharusnya selalu baru dan sulit diprediksi. Server membuatnya saat menyusun respons HTTP, lalu memakai nilai yang sama pada header CSP dan elemen skrip yang disetujui di respons tersebut.
Bentuk minimalnya seperti ini:
Content-Security-Policy: script-src 'nonce-r4nd0mValueHere'<script nonce="r4nd0mValueHere">
bootApplication();
</script>Nilai ilustratif di atas sengaja bukan resep pembuatan nonce. Kode produksi perlu memakai sumber acak yang aman secara kriptografis dengan entropi yang memadai untuk kebutuhan keamanan sistem.
Sifat pentingnya adalah nilai baru per respons. Pemakaian nonce tetap pada banyak respons mengubahnya menjadi token berumur panjang yang dapat disalin setelah terlihat. Nonce bukan rahasia bagi penerima halaman; fungsinya adalah sebagai kapabilitas yang dibatasi pada respons dokumen tempat browser menerapkan policy.
Server harus memasangnya secara selektif
Pembuatan nonce hanya separuh dari rancangan. Lapisan template harus menambahkan nonce pada skrip yang memang hendak diizinkan aplikasi, tanpa menambahkannya secara otomatis ke elemen skrip yang dipengaruhi penyerang.
Misalnya, template mengeluarkan blok bootstrap tepercaya:
<script nonce="{{ csp_nonce }}">
window.appConfig = {{ trusted_config_json }};
</script>Atribut nonce aman hanya jika csp_nonce berasal dari state respons di sisi server dan template menjaga batas atribut dengan benar. Nilai konfigurasi juga memerlukan encoding yang sesuai dengan konteks JavaScript. CSP tidak membuat interpolasi yang tidak aman menjadi aman.
Abstraksi yang berbahaya adalah proses yang memindai markup hasil render lalu menambahkan nonce aktif ke setiap elemen <script>. Jika celah injeksi dapat membuat elemen skrip sebelum transformasi itu berjalan, aplikasi justru dapat memberi izin pada elemen hasil injeksi. Otorisasi sebaiknya mengikuti asal kode template tepercaya, bukan sekadar nama tag.
Nonce mengubah posisi kode inline
Tanpa sumber nonce atau hash, script-src yang restriktif pada umumnya memblokir blok skrip inline dan event handler inline. Sumber nonce memberi elemen skrip tertentu jalur eksplisit melewati pembatasan tersebut.
Pendekatan ini memudahkan migrasi aplikasi yang masih memerlukan sedikit kode bootstrap inline hasil render server. Alih-alih mengaktifkan 'unsafe-inline' untuk seluruh skrip inline, aplikasi dapat menandai hanya blok yang memang diperlukan.
Policy dapat tetap eksplisit:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-<response-value>';
object-src 'none';
base-uri 'none'Directive yang dipakai harus mengikuti model resource aplikasi. Menyalin contoh policy tanpa inventaris skrip, worker, frame, koneksi, style, dan jenis resource lain dapat memutus fungsi yang dibutuhkan atau meninggalkan celah policy pada bagian lain.
Nonce tidak melakukan sanitasi isi skrip
Skrip yang memiliki nonce diperlakukan sebagai kode tepercaya oleh policy. Jika teks tidak tepercaya dimasukkan ke isi skrip tersebut dengan cara yang mengubah sintaks JavaScript, browser dapat menjalankan kode yang terbentuk karena elemen skrip induknya sudah membawa nonce yang diterima.
Contohnya, penyusunan source JavaScript melalui penggabungan input mentah tetap berbahaya:
<script nonce="{{ csp_nonce }}">
const displayName = "UNTRUSTED_VALUE";
</script>Representasi yang aman bergantung pada aliran data dan framework. Serialisasi data terstruktur, encoding yang tepat untuk konteksnya, atau pemindahan data ke representasi non-eksekusi dapat mencegah data berubah menjadi teks program. Pokok batasnya adalah bahwa nonce memberi izin pada elemen skrip; nonce tidak memeriksa asal setiap byte di dalam elemen tersebut.
Eksposur nonce berbeda dari injeksi nonce
Penerima halaman dapat memeriksa dokumennya sendiri dan melihat nonce. Fakta itu sendiri tidak meruntuhkan kontrol. Penyerang yang mengeksploitasi celah injeksi dari jarak jauh tetap memerlukan jalur yang menghasilkan markup executable dengan nonce yang diterima di dokumen korban.
Kesalahan rancangan yang lebih serius terjadi saat input tidak tepercaya dapat mengisi atribut nonce, menyalin nonce ke markup executable buatan penyerang, atau masuk ke blok skrip tepercaya sebagai source yang dapat dieksekusi. Jalur seperti itu menghapus pemisahan yang hendak ditegakkan policy.
Karena itu, helper rendering di sisi server sebaiknya memperlakukan nonce sebagai metadata respons dengan API yang sempit. Meneruskannya melalui struktur data aplikasi yang umum atau membukanya ke fragmen template yang tidak berkaitan memperbesar jumlah tempat yang dapat memberi izin kode secara tidak sengaja.
Cache memerlukan penanganan khusus
Nonce per respons berinteraksi langsung dengan cache HTML. Jika cache menyimpan respons lengkap yang berisi header CSP sekaligus markup bernonce, pemutaran ulang respons tersebut juga memutar ulang nonce.
Kelayakan respons cache bergantung pada arsitektur, tetapi rancangan yang mensyaratkan nonce baru untuk setiap respons yang dikirim tidak dapat begitu saja menyimpan HTML dan header final tanpa batas. Pola yang umum adalah membuat atau mengganti nonce pada lapisan yang berjalan untuk setiap respons, atau tidak menyimpan dokumen final yang bersifat personal.
Nilai pada header dan body juga harus tetap sinkron. Mengubah hanya header atau hanya markup akan membuat skrip sah gagal melewati pemeriksaan policy.
Mode report-only membantu migrasi, bukan enforcement
Content-Security-Policy-Report-Only dapat mencatat pelanggaran policy tanpa memblokirnya. Mode ini berguna saat menginventarisasi perilaku skrip dan mencari kode yang masih bergantung pada izin luas.
Mode report-only bukan batas proteksi. Policy harus dikirim sebagai Content-Security-Policy agar browser benar-benar menerapkan pembatasannya. Laporan juga dapat berisi sinyal yang bising atau tidak lengkap, sehingga pemakaian operasional sebaiknya disertai penyaringan dan korelasi, bukan menganggap setiap event sebagai serangan yang sudah terkonfirmasi.
Rollout bertahap dapat bergerak dari observasi ke enforcement setelah jalur eksekusi yang dibutuhkan memiliki otorisasi eksplisit dan dependensi yang tidak diharapkan telah disingkirkan.
Nonce berada di dalam policy skrip yang lebih luas
Otorisasi berbasis nonce paling kuat ketika bagian lain dari policy skrip tidak membuka kembali jalur eksekusi yang terlalu luas. Menambahkan nonce sambil mempertahankan sumber yang permisif dapat menyisakan rute lain bagi eksekusi skrip.
Kondisi aplikasi di sekitarnya juga tetap penting. Sink injeksi DOM, konstruksi HTML yang tidak aman, dependensi rentan, dan celah template sisi server tetap relevan meski CSP sudah dipasang. CSP adalah lapisan mitigasi yang diterapkan browser untuk membatasi dampak sebagian jalur injeksi; CSP bukan pengganti perbaikan pada jalur tersebut.
Rancangan nonce yang disiplin menjaga batas tetap kecil: buat nilai baru untuk respons, tempatkan nilai itu pada policy yang benar-benar diterapkan, pasang hanya pada elemen skrip yang dimaksudkan, pertahankan encoding data yang aman di dalam elemen tersebut, dan selaraskan perilaku cache dengan kebutuhan nonce baru. Dengan pola itu, kode inline memperoleh izin eksekusi eksplisit tanpa memberikan hak yang sama kepada setiap skrip inline yang masuk ke dokumen.