Kebijakan keamanan konten

Kebijakan Keamanan Konten dapat mengurangi risiko dan dampak serangan skrip lintas situs secara signifikan di browser modern.

Joe Medley
Joe Medley

Dukungan Browser

  • 25
  • 14
  • 23
  • 7

Sumber

Model keamanan web didasarkan pada kebijakan origin yang sama. Misalnya, kode dari https://mybank.com hanya boleh memiliki akses ke data https://mybank.com, dan https://evil.example.com tidak boleh mendapatkan akses yang diizinkan. Secara teori, setiap origin tetap terisolasi dari web lainnya sehingga memberikan sandbox yang aman bagi developer untuk membangunnya. Namun dalam praktiknya, penyerang telah menemukan beberapa cara untuk menumbangkan sistem.

Serangan pembuatan skrip lintas situs (XSS), misalnya, mengabaikan kebijakan origin yang sama dengan mengelabui situs agar mengirimkan kode berbahaya bersama dengan konten yang dimaksud. Ini adalah masalah besar, karena browser memercayai semua kode yang muncul di halaman sebagai bagian sah dari asal keamanan halaman tersebut. Tips Praktis XSS adalah lintas bagian yang sudah lama tetapi representatif dari metode yang dapat digunakan penyerang untuk melanggar kepercayaan ini dengan memasukkan kode berbahaya. Jika penyerang berhasil memasukkan kode apa pun, berarti mereka telah membobol sesi pengguna dan mendapatkan akses ke informasi pribadi.

Halaman ini menguraikan Kebijakan Keamanan Konten (CSP) sebagai strategi untuk mengurangi risiko dan dampak serangan XSS di browser modern.

Komponen CSP

Untuk menerapkan CSP yang efektif, lakukan langkah-langkah berikut:

  • Gunakan daftar yang diizinkan untuk memberi tahu klien apa saja yang diizinkan dan tidak diizinkan.
  • Ketahui perintah yang tersedia.
  • Pelajari kata kunci yang digunakannya.
  • Batasi penggunaan kode inline dan eval().
  • Laporkan pelanggaran kebijakan ke server Anda sebelum menerapkannya.

Daftar sumber yang diizinkan

Serangan XSS mengeksploitasi ketidakmampuan browser untuk membedakan antara skrip yang merupakan bagian dari aplikasi Anda dan skrip yang telah dimasukkan dengan niat jahat oleh pihak ketiga. Misalnya, tombol Google +1 di bagian bawah halaman ini memuat dan menjalankan kode dari https://apis.google.com/js/plusone.js dalam konteks asal halaman ini. Kami memercayai kode tersebut, tetapi kami tidak dapat berharap browser mengetahui sendiri bahwa kode dari apis.google.com aman untuk dijalankan, sedangkan kode dari apis.evil.example.com mungkin tidak. Browser dengan mudah mendownload dan menjalankan kode apa pun yang diminta halaman, terlepas dari sumbernya.

Header HTTP Content-Security-Policy CSP memungkinkan Anda membuat daftar sumber konten tepercaya yang diizinkan, dan memberi tahu browser untuk menjalankan atau merender hanya resource dari sumber tersebut. Meskipun penyerang dapat menemukan lubang untuk memasukkan skrip, skrip tidak akan cocok dengan daftar yang diizinkan, sehingga tidak akan dieksekusi.

Kami memercayai apis.google.com untuk memberikan kode yang valid, dan kami percaya diri kami untuk melakukan hal yang sama. Berikut adalah contoh kebijakan yang memungkinkan skrip dijalankan hanya jika skrip berasal dari salah satu dari dua sumber tersebut:

Content-Security-Policy: script-src 'self' https://apis.google.com

script-src adalah perintah yang mengontrol serangkaian hak istimewa terkait skrip untuk sebuah halaman. Header ini 'self' sebagai satu sumber skrip yang valid, dan https://apis.google.com sebagai satu sumber skrip yang lain. Browser kini dapat mendownload dan mengeksekusi JavaScript dari apis.google.com melalui HTTPS, juga dari asal halaman saat ini, tetapi bukan dari origin lain. Jika penyerang memasukkan kode ke situs Anda, browser akan menampilkan error dan tidak menjalankan skrip yang dimasukkan.

Error konsol: Menolak untuk memuat skrip 'http://evil.example.com/evil.js' karena melanggar perintah Kebijakan Keamanan Konten berikut: script-src 'self' https://apis.google.com
Konsol menampilkan error saat skrip mencoba dijalankan dari origin yang tidak ada dalam daftar yang diizinkan.

Kebijakan berlaku untuk berbagai sumber daya

CSP menyediakan serangkaian perintah kebijakan yang memungkinkan kontrol terperinci atas resource yang boleh dimuat halaman, termasuk script-src dari contoh sebelumnya.

Daftar berikut menguraikan perintah resource lainnya mulai level 2. Spesifikasi level 3 telah draf, tetapi sebagian besar tidak diimplementasikan di browser utama.

base-uri
Membatasi URL yang dapat muncul di elemen <base> halaman.
child-src
Mencantumkan URL untuk pekerja dan konten frame yang disematkan. Misalnya, child-src https://youtube.com memungkinkan penyematan video dari YouTube, tetapi tidak dari asal lain.
connect-src
Membatasi origin yang dapat Anda hubungkan menggunakan XHR, WebSockets, dan EventSource.
font-src
Menentukan origin yang dapat menyediakan font web. Misalnya, Anda dapat mengizinkan font web Google menggunakan font-src https://themes.googleusercontent.com.
form-action
Mencantumkan endpoint yang valid untuk pengiriman dari tag <form>.
frame-ancestors
Menentukan sumber yang dapat menyematkan halaman saat ini. Perintah ini berlaku untuk tag <frame>, <iframe>, <embed>, dan <applet>. Parameter ini tidak dapat digunakan dalam tag <meta> atau untuk resource HTML.
frame-src
Perintah ini tidak digunakan lagi di level 2, tetapi dipulihkan di level 3. Jika tidak ada, browser akan melakukan fallback ke child-src.
img-src
Menentukan asal image asal yang dapat dimuat.
media-src
Membatasi origin yang diizinkan untuk mengirimkan video dan audio.
object-src
Memungkinkan kontrol atas Flash dan plugin lainnya.
plugin-types
Membatasi jenis plugin yang dapat dipanggil oleh halaman.
report-uri
Menentukan URL yang dikirim browser saat kebijakan keamanan konten dilanggar. Perintah ini tidak dapat digunakan di tag <meta>.
style-src
Membatasi origin halaman yang dapat menggunakan stylesheet.
upgrade-insecure-requests
Menginstruksikan agen pengguna untuk menulis ulang skema URL dengan mengubah HTTP ke HTTPS. Perintah ini ditujukan untuk situs dengan sejumlah besar URL lama yang perlu ditulis ulang.
worker-src
Perintah CSP Level 3 yang membatasi URL yang dapat dimuat sebagai pekerja, pekerja bersama, atau pekerja layanan. Mulai Juli 2017, perintah ini memiliki implementasi terbatas.

Secara default, browser memuat resource terkait dari asal apa pun, tanpa batasan, kecuali jika Anda menetapkan kebijakan dengan perintah tertentu. Untuk mengganti perintah default, tentukan perintah default-src. Perintah ini menentukan default untuk setiap perintah yang tidak ditentukan yang diakhiri dengan -src. Misalnya, jika Anda menetapkan default-src ke https://example.com dan tidak menentukan perintah font-src, Anda hanya dapat memuat font dari https://example.com.

Perintah berikut tidak menggunakan default-src sebagai penggantian. Perlu diingat bahwa gagal menyetelnya sama saja dengan mengizinkan apa pun:

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

Sintaksis CSP dasar

Untuk menggunakan perintah CSP, cantumkan di header HTTP dengan perintah yang dipisahkan dengan titik dua. Pastikan untuk mencantumkan semua resource yang diperlukan dari jenis tertentu dalam satu perintah sebagai berikut:

script-src https://host1.com https://host2.com

Berikut adalah contoh beberapa perintah, dalam hal ini untuk aplikasi web yang memuat semua resource-nya dari jaringan penayangan konten (CDN) di https://cdn.example.net, dan tidak menggunakan konten atau plugin berbingkai:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

Detail implementasi

Browser modern mendukung header Content-Security-Policy tanpa awalan. Ini adalah header yang direkomendasikan. Header X-WebKit-CSP dan X-Content-Security-Policy yang mungkin Anda lihat dalam tutorial online tidak digunakan lagi.

CSP ditentukan berdasarkan halaman demi halaman. Anda harus mengirim {i>header<i} HTTP dengan setiap respons yang ingin Anda lindungi. Dengan begitu, Anda dapat menyesuaikan kebijakan untuk halaman tertentu berdasarkan kebutuhan spesifiknya. Misalnya, jika satu kumpulan laman di situs Anda memiliki tombol +1, sementara yang lainnya tidak, Anda dapat mengizinkan kode tombol dimuat hanya saat diperlukan.

Daftar sumber untuk setiap perintah bersifat fleksibel. Anda dapat menentukan sumber berdasarkan skema (data:, https:), atau dengan rentang kekhususan dari hanya nama host (example.com, yang cocok dengan origin apa pun di host tersebut: skema apa pun, port apa pun) hingga URI yang sepenuhnya memenuhi syarat (https://example.com:443, yang hanya cocok dengan HTTPS, hanya example.com, dan hanya port 443). Karakter pengganti diterima, tetapi hanya sebagai skema, port, atau di posisi paling kiri pada nama host: *://*.example.com:* akan cocok dengan semua subdomain example.com (tetapi bukan example.com itu sendiri), menggunakan skema apa pun, pada port mana pun.

Daftar sumber juga menerima empat kata kunci:

  • 'none' tidak cocok dengan apa pun.
  • 'self' cocok dengan asal saat ini, tetapi tidak dengan subdomainnya.
  • 'unsafe-inline' mengizinkan JavaScript dan CSS inline. Untuk mengetahui informasi selengkapnya, lihat Menghindari kode inline.
  • 'unsafe-eval' mengizinkan mekanisme teks ke JavaScript seperti eval. Untuk mengetahui informasi selengkapnya, lihat Menghindari eval().

Kata kunci ini memerlukan tanda kutip tunggal. Misalnya, script-src 'self' (dengan tanda kutip) mengotorisasi eksekusi JavaScript dari host saat ini; script-src self (tanpa tanda kutip) memungkinkan JavaScript dari server bernama "self" (dan bukan dari host saat ini), yang mungkin bukan yang Anda maksud.

Lakukan sandbox halaman Anda

Ada satu perintah lagi yang perlu dibahas: sandbox. Ini sedikit berbeda dari lainnya yang telah kita lihat, karena halaman ini menerapkan pembatasan pada tindakan yang dapat dilakukan halaman, bukan pada resource yang dapat dimuat oleh halaman. Jika ada perintah sandbox, halaman akan diperlakukan seolah-olah dimuat di dalam <iframe> dengan atribut sandbox. Hal ini dapat memiliki berbagai efek pada halaman: memaksa halaman ke asal yang unik, dan mencegah pengiriman formulir, di antaranya. Bagian ini sedikit di luar cakupan halaman ini, tetapi Anda dapat menemukan detail lengkap tentang atribut sandbox yang valid di bagian "Sandboxing" pada spesifikasi HTML5.

Tag meta

Mekanisme pengiriman yang disukai CSP adalah header HTTP. Namun, sebaiknya tetapkan kebijakan di halaman secara langsung di markup. Lakukan hal tersebut menggunakan tag <meta> dengan atribut http-equiv:

<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">

Tidak dapat digunakan untuk frame-ancestors, report-uri, atau sandbox.

Menghindari kode inline

Sehebat daftar yang diizinkan berbasis origin yang digunakan dalam perintah CSP, daftar tersebut tidak dapat mengatasi ancaman terbesar yang ditimbulkan oleh serangan XSS: injeksi skrip inline. Jika penyerang dapat menginjeksikan tag skrip yang secara langsung berisi beberapa payload berbahaya (seperti <script>sendMyDataToEvilDotCom()</script>), browser tidak dapat membedakannya dari tag skrip inline yang sah. CSP mengatasi masalah ini dengan memblokir skrip inline sepenuhnya.

Pemblokiran ini tidak hanya mencakup skrip yang disematkan langsung dalam tag script, tetapi juga pengendali peristiwa inline dan URL javascript:. Anda harus memindahkan konten tag script ke file eksternal, serta mengganti URL javascript: dan <a ... onclick="[JAVASCRIPT]"> dengan panggilan addEventListener() yang sesuai. Misalnya, Anda dapat menulis ulang kode berikut dari:

<script>
    function doAmazingThings() {
    alert('YOU ARE AMAZING!');
    }
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>

menjadi seperti:

<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
    alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('amazing')
    .addEventListener('click', doAmazingThings);
});

Kode yang ditulis ulang tidak hanya kompatibel dengan CSP, tetapi juga selaras dengan praktik terbaik desain web. JavaScript inline menggabungkan struktur dan perilaku dengan cara yang membuat kode membingungkan. Juga lebih rumit untuk membuat cache dan kompilasi. Memindahkan kode Anda ke resource eksternal akan membuat performa halaman Anda lebih baik.

Memindahkan tag dan atribut style inline ke stylesheet eksternal juga sangat direkomendasikan untuk melindungi situs Anda dari serangan pemindahan data yang tidak sah berbasis CSS.

Cara mengizinkan skrip dan gaya inline untuk sementara

Anda dapat mengaktifkan skrip dan gaya inline dengan menambahkan 'unsafe-inline' sebagai sumber yang diizinkan dalam direktif script-src atau style-src. CSP Level 2 juga memungkinkan Anda menambahkan skrip inline tertentu ke daftar yang diizinkan menggunakan nonce kriptografis (angka yang digunakan satu kali) atau hash sebagai berikut.

Untuk menggunakan nonce, berikan atribut nonce pada tag skrip. Nilainya harus sama dengan salah satu dalam daftar sumber tepercaya. Contoh:

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
    // Some inline code I can't remove yet, but need to as soon as possible.
</script>

Tambahkan nonce ke perintah script-src dengan mengikuti kata kunci nonce-:

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

Nonce harus dibuat untuk setiap permintaan halaman, dan harus tidak dapat ditebak.

{i>Hash<i} bekerja dengan cara yang serupa. Daripada menambahkan kode ke tag skrip, buatlah hash SHA skrip itu sendiri dan tambahkan ke perintah script-src. Misalnya, jika halaman Anda berisi hal ini:

<script>alert('Hello, world.');</script>

Kebijakan Anda harus berisi hal berikut:

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

Awalan sha*- menentukan algoritma yang menghasilkan hash. Contoh sebelumnya menggunakan sha256-, tetapi CSP juga mendukung sha384- dan sha512-. Saat membuat hash, hapus tag <script>. Pentingnya huruf besar dan spasi kosong, termasuk spasi kosong di awal dan akhir.

Solusi untuk menghasilkan hash SHA tersedia dalam berbagai bahasa. Dengan menggunakan Chrome 40 atau yang lebih baru, Anda dapat membuka DevTools lalu memuat ulang halaman. Tab Konsol menampilkan pesan error dengan hash SHA-256 yang benar untuk setiap skrip inline Anda.

Menghindarineval()

Meskipun tidak dapat memasukkan skrip secara langsung, penyerang mungkin dapat mengelabui aplikasi Anda agar mengubah teks input menjadi JavaScript yang dapat dieksekusi dan mengeksekusinya atas nama mereka. eval(), new Function(), setTimeout([string], …), dan setInterval([string], ...) adalah semua vektor yang dapat digunakan penyerang untuk mengeksekusi kode berbahaya melalui teks yang dimasukkan. Respons default CSP terhadap risiko ini adalah memblokir sepenuhnya semua vektor tersebut.

Hal ini memiliki efek berikut pada cara Anda membangun aplikasi:

  • Anda harus mengurai JSON menggunakan JSON.parse bawaan, bukan mengandalkan eval. Operasi JSON yang aman tersedia di setiap browser sejak IE8.
  • Anda harus menulis ulang panggilan setTimeout atau setInterval yang Anda lakukan menggunakan fungsi inline, bukan string. Misalnya, jika halaman Anda berisi hal berikut:

    setTimeout("document.querySelector('a').style.display = 'none';", 10);
    

    Tulis ulang sebagai:

    setTimeout(function () {
        document.querySelector('a').style.display = 'none';
    }, 10);
      ```
    
  • Hindari pembuatan template inline saat runtime. Banyak library pembuatan template menggunakan new Function() sering kali untuk mempercepat pembuatan template saat runtime, sehingga memungkinkan evaluasi teks berbahaya. Beberapa framework siap pakai untuk mendukung CSP, dan beralih kembali ke parser yang andal tanpa eval. Perintah ng-csp AngularJS adalah contoh yang bagus. Namun, sebaiknya gunakan bahasa template yang menawarkan prakompilasi, seperti Handlebars. Prakompilasi template dapat membuat pengalaman pengguna menjadi lebih cepat daripada implementasi runtime tercepat, serta membuat situs Anda lebih aman.

Jika eval() atau fungsi teks ke JavaScript lainnya sangat penting untuk aplikasi, Anda dapat mengaktifkannya dengan menambahkan 'unsafe-eval' sebagai sumber yang diizinkan dalam perintah script-src. Kami sangat tidak menyarankan hal ini karena risiko injeksi kode yang ditimbulkannya.

Melaporkan pelanggaran kebijakan

Untuk memberi tahu server bug yang mungkin memungkinkan injeksi berbahaya, Anda dapat memberi tahu browser untuk POST laporan pelanggaran berformat JSON ke lokasi yang ditentukan dalam perintah report-uri:

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

Laporan ini akan terlihat seperti berikut:

{
    "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
    }
}

Laporan ini berisi informasi yang berguna untuk menemukan penyebab pelanggaran kebijakan, termasuk halaman saat itu terjadi (document-uri), referrer halaman tersebut, resource yang melanggar kebijakan halaman (blocked-uri), perintah spesifik yang dilanggar (violated-directive), dan kebijakan lengkap halaman (original-policy).

Laporan Saja

Jika Anda baru mulai menggunakan CSP, sebaiknya gunakan mode hanya laporan untuk mengevaluasi status aplikasi sebelum mengubah kebijakan. Untuk melakukannya, kirim header Content-Security-Policy-Report-Only, bukan mengirim header Content-Security-Policy:

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

Kebijakan yang ditentukan dalam mode hanya laporan tidak memblokir resource yang dibatasi, tetapi mengirimkan laporan pelanggaran ke lokasi yang Anda tentukan. Anda bahkan dapat mengirim kedua header untuk menerapkan satu kebijakan sambil memantau kebijakan yang lain. Ini adalah cara yang bagus untuk menguji perubahan pada CSP Anda sambil menerapkan kebijakan Anda saat ini: aktifkan pelaporan untuk kebijakan baru, pantau laporan pelanggaran dan perbaiki bug, dan jika Anda sudah puas dengan kebijakan baru ini, mulailah menerapkannya.

Penggunaan di Dunia Nyata

Langkah pertama dalam membuat kebijakan untuk aplikasi Anda adalah mengevaluasi resource yang dimuatnya. Setelah memahami struktur aplikasi, buat kebijakan berdasarkan persyaratannya. Bagian berikut memandu beberapa kasus penggunaan umum dan proses keputusan untuk mendukungnya dengan mengikuti pedoman CSP.

Widget media sosial

  • Tombol Suka Facebook memiliki beberapa opsi implementasi. Sebaiknya gunakan versi <iframe> agar tetap di-sandbox dari bagian lain situs Anda. Alat ini memerlukan perintah child-src https://facebook.com agar berfungsi dengan baik.
  • Tombol Tweet X bergantung pada akses ke skrip. Pindahkan skrip yang disediakan ke file JavaScript eksternal, dan gunakan perintah script-src https://platform.twitter.com; child-src https://platform.twitter.com.
  • Platform lain memiliki persyaratan serupa, dan dapat ditangani dengan cara serupa. Untuk menguji resource ini, sebaiknya setel default-src dari 'none' dan tonton konsol Anda untuk menentukan resource mana yang perlu diaktifkan.

Untuk menggunakan beberapa widget, gabungkan perintah sebagai berikut:

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

Karantina wilayah

Untuk beberapa situs, sebaiknya pastikan hanya resource lokal yang dapat dimuat. Contoh berikut mengembangkan CSP untuk situs perbankan, dimulai dengan kebijakan default yang memblokir semuanya (default-src 'none').

Situs ini memuat semua gambar, gaya, dan skrip dari CDN di https://cdn.mybank.net, dan terhubung ke https://api.mybank.com/ menggunakan XHR untuk mengambil data. Proses ini menggunakan bingkai, tetapi hanya untuk halaman yang bersifat lokal ke situs (tidak ada asal pihak ketiga). Tidak ada Flash di situs, tidak ada font, tidak ada tambahan. Header CSP paling ketat yang dapat dikirim adalah ini:

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

Khusus SSL

Berikut adalah contoh CSP untuk administrator forum yang ingin memastikan bahwa semua resource di forumnya hanya dimuat menggunakan saluran yang aman, tetapi tidak berpengalaman dalam melakukan coding dan tidak memiliki resource untuk menulis ulang software forum pihak ketiga yang penuh dengan skrip dan gaya inline:

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

Meskipun https: ditetapkan dalam default-src, perintah skrip dan gaya tidak secara otomatis mewarisi sumber tersebut. Setiap perintah menimpa default untuk jenis resource tertentu.

Pengembangan standar CSP

Kebijakan Keamanan Konten Level 2 adalah standar yang direkomendasikan W3C. Web Application Security Working Group W3C sedang mengembangkan iterasi berikutnya dari spesifikasi ini, Content Security Policy Level 3.

Untuk mengikuti diskusi seputar fitur mendatang ini, lihat arsip milis publik-webappsec@.