Kemarin lusa (1/22), saya menerima laporan dari admin website kementerian yang saya buat kalau webnya mengalami kendala hanya menampilkan preloader saja. Setelah saya cek melalui devtools browser, pada bagian console, saya mendapati kalau browser tidak diizinkan untuk me-load javascript, css, maupun font yang berasal dari cdnjs dan unpkg. Alhasil sebagian script yang dibutuhkan tidak di-load, yaudah, rusak deh. Kurang lebih penampakan errornya (di Firefox) kayak gini:
Ternyata sebelumnya dari pihak IT pusat sana, web servernya baru disetting untuk mengirimkan header Content-Security-Policy
yang berisikan whitelist host mana saja yang diperbolehkan untuk di-load oleh website-website yang berada pada server naungan mereka, dan cdnjs.cloudflare.com serta unpkg.com tidak terdaftar dalam whitelist tersebut. Karena saya cuma dikasih akses ke sebagian fitur cpanel-nya aja, alhasil yang saya lakukan adalah mengunduh semua resource pada CDN, dan mengunggahnya ke server mereka, lalu mengubah kode HTML dari yang tadinya load ke URL CDN menjadi load ke URL website itu sendiri.
Apa yang dilakukan IT pihak pusat memang sudah benar, yaitu mencegah potensi XSS dari berbagai website yang dikembangkan berbagai developer (dan vendor) yang hosting websitenya ke server mereka, dimana jika ada salah satu website saja vuln, maka website lain bisa terpengaruh karenanya.
Setelah kejadian ini saya baca-baca lagi tentang Content-Security-Policy (CSP), dan supaya menjadi manfaat karena blog saya udah lama ga update, saya tulis sekalian disini.
Setelah ini saya akan singkat Content-Security-Policy menjadi CSP saja.
Apa itu CSP?
CSP adalah HTTP Response Header atau Meta Tag untuk mengatur resource dari sumber mana saja yang diperbolehkan untuk dieksekusi oleh web browser (yang mendukung CSP tentunya).
Yang dimaksud resource disini itu ya web resource, seperti javascript, css, font, image, video, audio, dsb.
Dengan CSP kita dapat meminimalisir potensi-potensi serangan XSS pada website kita dengan memerintahkan browser untuk jangan me-load dan mengeksekusi resource dari sumber yang tidak kita inginkan.
Apa yang Bisa Kita Lakukan dengan CSP?
Dengan memberikan CSP ke halaman web kamu, kamu dapat:
- Mencegah browser untuk mengeksekusi inline script (javascript di HTML).
- Mencegah browser untuk mengeksekusi
eval
. - Mencegah browser untuk mengeksekusi inline style.
- Mencegah browser untuk mengeksekusi script, css, font, gambar, video, suara, dsb diluar host yang kita tentukan.
- Mencegah browser untuk submisi form yang
action
-nya menuju ke host yang tidak kita daftarkan. - Mencegah browser untuk menjalankan Ajax, maupun WebSocket diluar host yang kita tentukan.
- dll.
Bagaimana CSP dapat Mencegah XSS?
Serangan XSS umumnya berasal dari form yang disubmit oleh user, yang kemudian isian dari form tersebut di-render ke user lain sebagai kode HTML (tidak di-escaping).
Contoh sederhananya adalah form komentar. Misalkan ada user memasukkan kode <script>alert('oi')</script>
pada kolom komentar, dan server-side tidak melakukan escaping terhadap data komentar tersebut, yang terjadi saat komentar tersebut di-render ke user lain adalah browser akan mengeksekusi inline script-nya dan menampilkan alert ke user.
Dengan CSP, kita dapat menghindari hal ini dengan cara memerintahkan browser untuk jangan eksekusi inline script, melalui response header ataupun meta tag si CSP. Jadi sekalipun developer khilaf tidak melakukan escaping, serangan XSS dapat dihindari.
Benefitnya akan sangat terasa kalau kamu punya website atau sedang mengelola server yang memiliki puluhan sampai ratusan halaman yang menampilkan data hasil inputan form didalamnya. Dengan CSP kamu cukup setting http server kamu (nginx/apache/dsb) untuk mengirimkan header CSP, dan boom! kamu tidak perlu lagi khawatir tentang XSS. Tapi sebelum melakukan ini, lebih baik kamu komunikasikan dulu hal ini ke tim developer, supaya ga ada kejadian seperti saya yang mengalami error saat production karena menggunakan resource dari sumber yang tidak "direstui".
Penulisan CSP
Seperti yang sudah saya bilang sebelumnya, CSP dapat kita tuliskan pada response header, ataupun meta tag. Hanya saja, artikel yang saya baca di halaman Google Developers lebih menyarankan menggunakan response header. Jadi disini saya akan mencontohkan menggunakan response header saja.
Untuk penulisan header CSP, formatnya adalah sebagai berikut:
Content-Security-Policy: <policy-directive-1>; <policy-directive-2>; <policy-directive-n>
Sedangkan untuk penulisan <policy-directive>
formatnya adalah seperti ini:
<directive-a> <source-1> <source-2> <source-n>
Sebagai contoh, jika kita hanya memperbolehkan load script dan css dari web-a.com
dan web-b.com
,
kita dapat menuliskan CSP seperti ini:
Content-Security-Policy: script-src https://web-a.com https://web-b.com; style-src https://web-a.com https://web-b.com
Pada contoh diatas kita menggunakan directive script-src
dan style-src
.
Dibawah ini adalah daftar beberapa directive yang dapat kamu gunakan:
connect-src
: membatasi akses ajax (XMLHttpRequest), WebSocket, dan EventSource.font-src
: membatasi akses font.img-src
: membatasi akses image.script-src
: membatasi akses script.style-src
: membatasi akses style (css).media-src
: membatasi akses video dan audio.worker-src
: membatasi akses service worker.child-src
: membatasi akses iframe dan worker.form-action
: membatasiaction
dari form.default-src
: kalau kamu malas jabarkan satu-persatu, kamu bisa gunakan ini untuk membatasi semua hal diatas.
Daftar diatas bisa saja bertambah atau berubah suatu saat, untuk itu, selebihnya bisa kamu pelajari sendiri pada artikel CSP di Google Developers atau panduan CSP di MDN.
Selanjutnya, selain mendaftarkan host apa saja yang diperbolehkan, CSP juga memiliki beberapa keyword yang dapat kamu gunakan sebagai <source>
seperti dibawah ini:
'self'
: untuk memperbolehkan browser load resource dari domain yang sama dengan website di-load.'unsafe-inline'
: untuk memperbolehkan browser mengeksekusi inline script ataupun inline style.'unsafe-eval'
: untuk memperbolehkan browser mengeksekusi fungsi javascripteval
.'none'
: jika kamu tidak memperbolehkan browser load resource dari sumber manapun.
Keyword diatas harus kamu tulis dengan menyertakan tanda petik tunggal.
Contoh Penerapan CSP
Pada bagian ini saya menuliskan contoh-contoh CSP umum yang mungkin banyak orang butuhkan.
Disini dimisalkan saya host halaman-halaman contoh ini pada https://www.emsifa.com
.
Disini saya akan menggunakan PHP supaya lebih ringkas aja.
1. Memperbolehkan Semua Jenis Resource dari Domain yang Sama
Dari kebutuhan diatas, kita dapat terjemahkan menjadi:
- Semua Jenis Resource:
default-src
- Dari Domain yang Sama:
'self'
Jadi penerapan CSP-nya adalah sebagai berikut:
<?php
header("Content-Security-Policy: default-src 'self'");
?>
<html>
<body>
<script src="https://www.emsifa.com/js/app.js"></script>
<script src="https://cdnjs.cloudflare.com/jquery/jquery.min.js"></script>
<script>alert('hello')</script>
</body>
</html>
Pada contoh diatas, script app.js
akan di-load dan dieksekusi oleh browser, sedangkan script jQuery tidak akan dieksekusi karena berada pada host cdnjs.cloudflare.com
, dan script alert('hello')
dibawahnya juga tidak akan dieksekusi karena kita tidak mendaftarkan 'unsafe-inline'
pada header CSP.
2. Memperbolehkan Semua Jenis Resource dari Sumber yang Sama dan CDN.js
Dari kebutuhan diatas, kalau kita terjemahkan kedalam CSP, menjadi:
- Semua Jenis Resource:
default-src
- Sumber yang sama dan CDN.js:
'self' https://cdnjs.cloudflare.com
Kalau kita terapkan ke CSP akan menjadi:
<?php
header("Content-Security-Policy: default-src 'self' https://cdnjs.cloudflare.com");
?>
<html>
<body>
<script src="https://www.emsifa.com/js/app.js"></script>
<script src="https://cdnjs.cloudflare.com/jquery/jquery.min.js"></script>
<script>alert('hello')</script>
</body>
</html>
Pada contoh diatas, app.js
dan jQuery akan di-load oleh browser, sedangkan script alert('hello')
tetap diblokir oleh browser.
3. Hanya Memperbolehkan Script, CSS, dan Image dari Hosting Sendiri, tidak Memperbolehkan Video dan Audio Apapun, tapi memperbolehkan Embed Video Youtube
Oke, kita coba terjemahkan dulu kebutuhan diatas:
- Hanya Memperbolehkan Script CSS dan Image dari Hosting sendiri: artinya kita memerlukan 3 buah directive yaitu
script-src
,style-src
, danimg-src
yang memperbolehkan'self'
. - Tidak memperbolehkan Video dan Audio Apapun: artinya kita perlu menuliskan directive
media-src
menjadi'none'
. - Tapi memperbolehkan Embed Video Youtube: artinya kita perlu menuliskan directive
child-src
kehttps://youtube.com
.
Kalau kita terapkan ke header CSP menjadi seperti ini:
<?php
header("Content-Security-Policy: script-src 'self'; style-src 'self'; img-src: 'self'; media-src 'none'; child-src https://youtube.com");
?>
<html>
<head>
<link rel="stylesheet" href="https://www.emsifa.com/css/style.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/bootstrap/css/bootstrap.min.css"/>
<style>
img { width: 100px; }
</style>
</head>
<body>
<img src="https://www.emsifa.com/img/logo.png"/>
<img src="https://www.w3schools.com/img/logo.png"/>
<iframe width="560" height="315" src="https://www.youtube.com/embed/pTGRpH2dvRM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<script src="https://www.emsifa.com/js/app.js"></script>
<script src="https://cdnjs.cloudflare.com/jquery/jquery.min.js"></script>
<script>alert('hello')</script>
</body>
</html>
Dari seluruh resource pada kode HTML diatas, hasilnya adalah seperti ini:
- style
https://www.emsifa.com/css/style.css
: ✔. - style
https://cdnjs.cloudflare.com/bootstrap/css/bootstrap.min.css
: ❌. - inline style
img {width: 100px}
: ❌. - image
https://www.emsifa.com/img/logo.png
: ✔. - image
https://www.w3schools.com/img/logo.png
: ❌. - iframe
https://www.youtube.com/embed/pTGRpH2dvRM
: ✔. - script
https://www.emsifa.com/js/app.js
: ✔. - script
https://cdnjs.cloudflare.com/jquery/jquery.min.js
: ❌. - inline script
alert('hello')
: ❌.
Penutup
Jadi seperti itulah CSP, bagaimana penulisannya, serta beberapa contohnya. Artikel ini saya adaptasi dari 2 link dibawah ini:
- https://developers.google.com/web/fundamentals/security/csp
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
Yaudah, sekian dulu artikel kali ini. Semoga bermanfaat, dah ~