Django 6.1 CSP: Terapkan Nonce dengan Aman di Template
Panduan menerapkan CSP bertahap di Django 6.1, memakai nonce pada template, dan menghindari reuse nonce akibat cache.
Hook
Satu tag <script> yang tidak seharusnya ada dapat mengubah halaman menjadi titik masuk serangan. Content Security Policy (CSP) memberi browser aturan tentang sumber daya yang boleh dimuat dan skrip inline mana yang boleh dijalankan—lapisan pertahanan yang bisa membantu membatasi dampak injeksi konten.
Django 6.0 memperkenalkan dukungan CSP bawaan. Di Django 6.1, penggunaan nonce pada template menjadi lebih praktis lewat tag {% csp_nonce_attr %}. Tag ini dapat menambahkan atribut nonce ke elemen <script> dan <link>, termasuk aset yang dirender dari form.media.
Kita akan menerapkan kebijakan secara bertahap: mulai dengan report-only, tinjau pelanggaran, lalu aktifkan enforcement. Sepanjang proses, satu aturan penting jangan sampai terlewat: nonce di header harus cocok dengan nonce di HTML untuk respons yang sama, dan tidak boleh dipakai ulang oleh cache halaman.
Problem/Context
CSP adalah standar keamanan browser yang membatasi sumber konten berdasarkan directive seperti default-src, script-src, dan style-src. Misalnya, script-src dapat mengizinkan skrip dari origin sendiri dan skrip inline yang membawa nonce yang cocok. Kebijakan yang disusun baik membantu memperkecil ruang gerak injeksi skrip, tetapi bukan pengganti escaping output atau kontrol akses server.
Dua header CSP mendukung penerapan bertahap:
Content-Security-Policymenegakkan kebijakan. Browser memblokir konten yang melanggarnya.Content-Security-Policy-Report-Onlymelaporkan pelanggaran tanpa memblokir konten. Ini berguna untuk menguji kebijakan, tetapi tidak melindungi dari pelanggaran aktif.
Nonce (number used once) adalah nilai acak yang mengizinkan elemen tertentu berjalan jika nilai atributnya cocok dengan sumber nonce dalam header CSP. Django membuat nonce untuk respons ketika dipakai melalui konfigurasi dan template CSP. Karena itu, jangan membuat nonce sendiri secara terpisah untuk HTML dan header.
Untuk nonce bekerja, ada tiga bagian yang perlu selaras: ContentSecurityPolicyMiddleware, directive CSP yang memuat CSP.NONCE, serta context processor CSP agar template dapat mengakses csp_nonce. Django 6.1 menambahkan {% csp_nonce_attr %} untuk memudahkan atribut nonce pada skrip, stylesheet, dan aset Media form.
Solution Walkthrough (code-heavy)
1. Aktifkan middleware dan mulai dengan report-only
Tambahkan middleware CSP ke MIDDLEWARE di settings.py:
MIDDLEWARE = [
# ... middleware lain
"django.middleware.csp.ContentSecurityPolicyMiddleware",
# ... middleware lain
]
Mulai dengan kebijakan report-only sambil menginventarisasi aset yang benar-benar dipakai aplikasi:
from django.utils.csp import CSP
SECURE_CSP_REPORT_ONLY = {
"default-src": [CSP.SELF],
}
Kebijakan tersebut mengirim header report-only, bukan header enforcement. Browser tetap dapat memuat konten yang melanggar; gunakan masa uji ini untuk mengidentifikasi skrip, gambar, font, atau koneksi yang perlu dipertimbangkan sebelum menegakkan aturan.
Jika ingin browser mengirim laporan ke aplikasi atau layanan monitoring, sertakan directive pelaporan, misalnya:
SECURE_CSP_REPORT_ONLY = {
"default-src": [CSP.SELF],
"report-uri": "/csp-reports/",
}
Path itu harus ditangani endpoint aplikasi atau layanan yang memang disiapkan untuk menerima laporan. Django dapat mengonfigurasi directive pelaporan, tetapi tidak menyediakan penerima, penyimpanan, atau pemroses laporan CSP secara otomatis. Validasi juga bahwa laporan benar-benar sampai sebelum mengandalkannya sebagai sinyal operasional.
2. Konfigurasikan nonce dan context processor
Setelah kebijakan awal ditinjau, tambahkan nonce ke script-src atau style-src. CSP.NONCE hanya berlaku untuk dua directive tersebut:
from django.utils.csp import CSP
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
}
Jika elemen <style> inline juga memerlukan nonce, atur directive style-src secara eksplisit:
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
"style-src": [CSP.SELF, CSP.NONCE],
}
Jangan menambahkan CSP.NONCE ke default-src atau directive lain. Kebijakan di atas hanya contoh; sesuaikan sumber yang diizinkan berdasarkan inventaris aset dan kebutuhan aplikasi.
Tambahkan context processor CSP ke TEMPLATES agar template memperoleh variabel csp_nonce:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {
"context_processors": [
# ... context processor lain
"django.template.context_processors.csp",
],
},
},
]
Konfigurasi nonce yang tidak lengkap—misalnya memakai CSP.NONCE tanpa context processor—dapat menyebabkan nonce tidak tersedia seperti yang diharapkan. Django 6.1 menambahkan system check untuk membantu menemukan beberapa kesalahan konfigurasi CSP; jalankan pemeriksaan proyek setelah mengubah pengaturan.
3. Pasang nonce pada skrip dan style inline
Untuk elemen inline, gunakan csp_nonce yang disediakan context processor:
<script nonce="{{ csp_nonce }}">
initializeDashboard();
</script>
<style nonce="{{ csp_nonce }}">
.dashboard { display: grid; }
</style>
Nilai yang dipasang pada elemen harus sama dengan nilai nonce yang middleware masukkan ke header CSP untuk respons itu. Jika skrip inline tidak perlu ada, pilihan yang lebih sederhana adalah menghapusnya atau memindahkannya ke file statis yang diizinkan oleh kebijakan.
4. Gunakan tag Django 6.1 untuk skrip, stylesheet, dan Media
Untuk elemen <script> dan <link> eksternal, tag bawaan Django 6.1 menambahkan atribut nonce ketika context processor CSP dikonfigurasi:
<script src="/static/app.js" {% csp_nonce_attr %}></script>
<link rel="stylesheet" href="/static/app.css" {% csp_nonce_attr %}>
Aset form yang dikumpulkan melalui Media juga dapat dirender dengan nonce:
{% csp_nonce_attr form.media %}
Gunakan bentuk tanpa argumen untuk menambahkan atribut ke elemen yang sedang ditulis sendiri. Gunakan bentuk dengan form.media ketika ingin tag tersebut merender aset Media form. Tag ini tidak menggantikan directive CSP: sumber aset tetap harus diizinkan oleh kebijakan yang berlaku.
5. Terapkan kebijakan secara bertahap
Setelah laporan report-only ditinjau dan sumber yang diperlukan dipahami, aktifkan enforcement dengan SECURE_CSP. Django juga mengizinkan kedua setting dipakai bersamaan: kebijakan yang sudah diuji dapat ditegakkan, sementara kebijakan baru dievaluasi dalam mode report-only.
SECURE_CSP = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
}
SECURE_CSP_REPORT_ONLY = {
"default-src": [CSP.SELF],
"script-src": [CSP.SELF, CSP.NONCE],
"style-src": [CSP.SELF, CSP.NONCE],
}
Contoh kedua ini hanya berguna jika kebijakan yang sedang diuji memang berbeda dari baseline enforcement. Uji alur penting—termasuk halaman admin, form dengan Media, dan integrasi pihak ketiga—sebelum memperketat directive. Pertimbangkan juga override kebijakan per-view: decorator CSP dapat menggantikan kebijakan dasar untuk view tersebut, bukan menggabungkan semua directive secara otomatis.
6. Hindari cache halaman yang memakai ulang nonce
Respons dengan nonce terdiri dari header dan HTML yang saling terkait. Jika cache halaman penuh menyimpan keduanya lalu menyajikannya berulang kali, nonce yang sama ikut digunakan kembali. Ini melemahkan tujuan nonce yang seharusnya unik per respons.
Jangan menyimpan respons ber-nonce dalam cache halaman penuh dengan cara yang memutar ulang nonce. Jika caching diperlukan, pilih desain yang memastikan nonce baru dibuat dan dipasang konsisten pada header serta HTML setiap respons, atau hindari kebutuhan skrip/style inline sehingga konten statis dapat dicache dengan aman. Uji jalur cache, proxy, dan CDN—bukan hanya render langsung Django—untuk memastikan tidak ada nonce lama yang terlayani ulang.
Trade-offs
- Report-only bukan mode aman untuk berhenti di sana. Ia membantu menemukan konflik kebijakan, tetapi tidak memblokir konten yang melanggar.
- Kebijakan CSP perlu dikalibrasi. Aturan terlalu ketat dapat merusak fitur; aturan terlalu longgar mengurangi manfaatnya. Gunakan pengujian alur dan data pelanggaran, bukan tebakan.
- Nonce memerlukan koordinasi konfigurasi. Directive, context processor, template, dan header harus bekerja bersama. Django 6.1 menyediakan tag yang lebih praktis dan system check, tetapi tetap perlu pengujian aplikasi.
- Pelaporan memerlukan infrastruktur sendiri. Django tidak menerima dan memproses laporan secara otomatis; siapkan endpoint atau layanan pihak ketiga jika butuh pengumpulan terpusat.
- Cache halaman penuh dan nonce tidak cocok tanpa desain khusus. Penggunaan ulang nilai nonce melemahkan perlindungan. Tinjau cache aplikasi maupun lapisan proxy/CDN.
- Override per-view dapat mengganti baseline. Periksa kebijakan lengkap di view yang memakai override, jangan mengasumsikan directive global otomatis diwariskan.
- CSP hanya satu lapisan pertahanan browser. Tetap lakukan escaping output kontekstual, validasi input, autentikasi, dan otorisasi—termasuk otorisasi pada resolver GraphQL. CSP bukan penggantinya.
Conclusion + CTA
Dukungan CSP bawaan Django membuat kebijakan browser lebih mudah dikelola, sementara Django 6.1 menyederhanakan penggunaan nonce pada skrip, stylesheet, dan aset Media form. Mulailah dengan report-only untuk memahami dampak kebijakan, lalu tegakkan aturan secara bertahap. Pastikan CSP.NONCE hanya berada di script-src atau style-src, context processor CSP tersedia, dan header serta HTML menggunakan nonce yang sama untuk setiap respons.
Yang paling mudah luput adalah caching: halaman yang diputar ulang beserta nonce-nya dapat meniadakan sebagian tujuan mekanisme tersebut. Uji alur cache end-to-end sebelum menerapkan kebijakan ke production.
Untuk detail implementasi, baca panduan CSP Django 6.1, referensi CSP, tag template bawaan, catatan rilis Django 6.1, dan catatan rilis Django 6.0. Kebijakan CSP apa yang paling sulit kamu uji sebelum enforcement? Ceritakan pendekatanmu.
