Nonce CSP Memindahkan Trust Script dari Hostname ke Markup Respons

Kebijakan script yang hanya berbasis hostname menjawab pertanyaan yang terlalu luas: lokasi jaringan mana yang boleh memasok JavaScript? Batas ini melemah ketika origin yang diizinkan juga menampung file yang dikendalikan pengguna, endpoint bergaya JSONP, resource script lama, atau konten lain yang tidak pernah dimaksudkan mendapat otoritas eksekusi.

Content Security Policy berbasis nonce mengubah unit trust tersebut. Alih-alih memberi otoritas eksekusi kepada setiap script dari host yang disetujui, server menempatkan nilai yang tidak dapat diprediksi pada policy dan pada elemen <script> tertentu yang diberi otorisasi untuk respons itu. Browser memeriksa hubungan tersebut sebelum mengeksekusi elemen.

Mekanisme ini bukan sanitasi input umum. Ini adalah batas eksekusi yang ditegakkan browser, dan kekuatannya bergantung pada lokasi nonce dibuat, tempat nilainya disalin, serta script mana yang diizinkan meneruskan trust.

Nonce mengikat policy ke elemen yang dipilih

Sebuah respons dapat membawa policy seperti:

Content-Security-Policy: script-src 'nonce-r4Nd0mV4lu3'; object-src 'none'; base-uri 'none'

Dokumen kemudian dapat menandai script yang memang dimaksudkan untuk berjalan:

<script nonce="r4Nd0mV4lu3" src="/assets/app.js"></script>

Nilai nonce bukan secret aplikasi bersama. Properti yang berguna adalah nilai tersebut segar dan tidak dapat diprediksi untuk respons tempat ia digunakan. Respons baru memerlukan nonce baru. Pemakaian ulang nilai tetap mengubah token otorisasi yang seharusnya berlaku untuk satu instance dokumen menjadi nilai yang dapat disalin ke respons lain.

Server juga tidak boleh menempelkan nonce secara mekanis ke setiap fragmen yang berbentuk script dalam markup hasil render. Jika HTML yang dikendalikan penyerang mencapai tahap template yang otomatis menghias setiap elemen <script> dengan nonce saat ini, script injeksi dapat memperoleh otoritas yang sama dengan script aplikasi. Policy kemudian menegakkan keputusan trust yang keliru dari sisi server.

Batas yang relevan bukan sekadar “nonce tersedia”, melainkan “nonce hanya disalin ke elemen script yang dipilih oleh logika rendering tepercaya.”

Host allowlist dan policy nonce memberi otorisasi pada objek berbeda

Host allowlist seperti:

Content-Security-Policy: script-src 'self' https://static.example.net

memberi otorisasi pengambilan script berdasarkan lokasi sumber. Setiap resource yang memenuhi source expression dapat masuk ke jalur eksekusi script, dengan tetap tunduk pada pemeriksaan browser lainnya.

Policy nonce memberi otorisasi pada elemen, bukan seluruh namespace sebuah host. Script eksternal dapat membawa nonce sebagaimana script inline. Perbedaan ini penting pada origin yang menyediakan lebih banyak konten daripada yang sebenarnya dimaksudkan policy keamanan untuk dieksekusi.

Perubahan ini juga memengaruhi pemeliharaan. Memindahkan script tepercaya antar-host tidak selalu mengharuskan perluasan source allowlist jika elemen tersebut tetap diberi otorisasi nonce dan policy yang diterapkan tidak menambahkan pembatas terpisah yang mencegah fetch. Sebaliknya, penambahan script baru ke halaman mengharuskan jalur rendering menandai elemen itu secara sengaja.

Policy nonce tidak membuat provenance script menjadi tidak relevan. Script yang diberi otorisasi tetap memiliki privilege script yang berjalan di dokumen. Jika script tersebut dikompromikan di sumbernya, dibangun dari dependency yang dikompromikan, atau memiliki jalur data-ke-code yang berbahaya, nonce valid tidak memperbaikinya. CSP mengontrol kelayakan eksekusi di browser; CSP bukan bukti integritas software.

strict-dynamic meneruskan trust melalui pembuatan script

Aplikasi modern sering memuat dependency script saat runtime. Tanpa aturan tambahan, bootstrap script yang diberi otorisasi nonce masih dapat terkena blokir CSP ketika membuat elemen script lanjutan yang URL-nya tidak diizinkan oleh aturan lain.

Source expression 'strict-dynamic' mengubah perilaku tersebut. Pada user agent yang mendukungnya, trust dari nonce atau hash valid dapat diteruskan ke script yang dimuat oleh root script tepercaya melalui elemen script yang tidak dimasukkan parser. Policy dapat berbentuk:

Content-Security-Policy: script-src 'nonce-r4Nd0mV4lu3' 'strict-dynamic'; object-src 'none'; base-uri 'none'

Ini merupakan perluasan trust yang signifikan. Bootstrap script tidak lagi hanya diberi otorisasi untuk mengeksekusi kodenya sendiri; keputusan pemuatan script yang dibuatnya juga dapat memberi otorisasi pada turunan. Kode yang mengubah string dari penyerang menjadi URL script menjadi sangat penting dalam model ini.

'strict-dynamic' juga mengubah perlakuan terhadap source expression pada implementasi CSP yang mendukungnya. Host source, scheme source, 'self', dan 'unsafe-inline' dalam directive script-src yang sama tidak bertindak sebagai allowlist script tambahan ketika 'strict-dynamic' aktif bersama nonce atau hash valid. Policy kompatibilitas masih dapat menyertakan expression lama untuk user agent dengan level CSP terdahulu, sehingga perilaku efektif dapat berbeda berdasarkan dukungan CSP.

Pertanyaan deployment-nya bersifat arsitektural: root script mana yang layak mendapat otoritas delegasi, dan apakah jalur dynamic loading miliknya dapat dipengaruhi data yang tidak tepercaya?

Pembuatan nonce berada pada batas respons

Nonce harus cukup sulit diprediksi agar penyerang yang dapat menyuntikkan markup tidak dapat menebak nilai yang diterima browser. Nilainya juga harus unik untuk setiap respons.

Syarat ini menempatkan pembuatan nonce dekat dengan konstruksi respons. Reverse proxy, application server, edge renderer, atau komponen respons tepercaya lain dapat membuat nilainya, tetapi nilai yang sama harus mencapai header CSP dan markup yang diberi otorisasi tanpa berubah menjadi konstanta global yang dapat dipakai ulang.

Caching membuat susunan ini lebih rumit. Full-page cache yang menyimpan HTML berisi nonce bersama header CSP yang cocok dapat memutar ulang nonce yang sama kepada banyak client. Cache yang mengubah header tetapi tidak HTML justru dapat menghasilkan nilai yang tidak cocok dan merusak script yang seharusnya berjalan. Sistem yang melakukan cache terhadap HTML hasil render memerlukan desain yang mempertahankan kesegaran per respons, misalnya substitusi pada tahap akhir di layer tepercaya, atau strategi CSP berbeda seperti hash untuk markup script yang benar-benar statis.

Arsitektur cache tepatnya bergantung pada deployment. Invariannya lebih sederhana: browser harus menerima satu respons yang konsisten secara internal, dan nonce dari respons itu tidak boleh menjadi nilai otorisasi yang dapat diprediksi untuk respons berikutnya.

Nonce dapat terlihat tanpa menjadi credential mandiri

Nonce muncul di dokumen dan tersedia bagi pemrosesan browser. Memperlakukannya seperti password menghasilkan threat model yang keliru. Proteksi nonce CSP dirancang agar fragmen markup yang disuntikkan tidak memperoleh otoritas eksekusi hanya karena fragmen itu berada di halaman.

Kegagalan utama bukan fakta bahwa nilainya dapat terlihat di lingkungan dokumen. Kegagalan utamanya adalah memberikan jalur bagi markup yang dikendalikan penyerang untuk memperoleh atau mewarisi nonce valid pada elemen executable, atau membiarkan script yang sudah tepercaya mengubah data dari penyerang menjadi script executable.

Pembedaan ini menjaga security review tetap berfokus pada alur rendering dan eksekusi, bukan pada upaya menyembunyikan nilai yang memang harus berpartisipasi dalam markup dokumen.

Laporan CSP menunjukkan event enforcement, bukan exploitability

Deployment dapat lebih dulu mengamati efek policy dengan Content-Security-Policy-Report-Only. Mode ini mencatat pelanggaran tanpa menegakkan policy, sehingga berguna untuk menemukan jalur script yang akan diblokir oleh policy yang direncanakan.

Violation report adalah telemetry. Script yang diblokir tidak membuktikan adanya percobaan eksploitasi, dan ketiadaan laporan tidak membuktikan semua jalur injeksi aman. Cakupan browser, konfigurasi reporting, extension, kegagalan jaringan, sampling, dan traffic aplikasi memengaruhi data yang sampai ke collector.

CSP enforced dan report-only juga memiliki fungsi berbeda. Mode report-only dapat menampilkan masalah kompatibilitas sebelum enforcement, tetapi tidak menyediakan batas eksekusi. Saat policy dipindahkan ke enforcement, browser mulai menolak eksekusi script yang melanggar policy aktif.

Batas terkuat memberi otoritas eksekusi secara sempit

CSP berbasis nonce paling bernilai ketika mengurangi markup yang dapat memperoleh otoritas eksekusi script. Hal itu memerlukan lebih dari sekadar menambahkan string acak ke header. Pembuatan nonce harus dilakukan per respons, rendering tepercaya harus mengontrol penempatan nonce, dan delegated loading melalui 'strict-dynamic' harus diperlakukan sebagai otoritas, bukan sekadar kemudahan.

Hasilnya adalah model trust yang lebih sempit daripada hostname allowlist yang luas. Lokasi jaringan tetap dapat berpengaruh pada transport dan software supply, tetapi keputusan eksekusi browser terikat pada markup respons tertentu. Pemisahan ini berguna karena batas origin dan batas otoritas script tidak selalu merupakan hal yang sama.