Membangun pengalaman yang kaya di web saat ini hampir tidak dapat dihindari melibatkan menyematkan komponen dan konten yang tidak Anda kendalikan sepenuhnya. Widget pihak ketiga dapat mendorong interaksi dan memainkan peran penting dalam keseluruhan pengalaman pengguna, dan konten buatan pengguna terkadang bahkan lebih penting daripada konten asli situs. Tidak melakukan keduanya bukanlah pilihan, tapi dapat meningkatkan risiko {i>Sesuatu yang BurukTM<i} terjadi di situs Anda. Masing-masing yang Anda sematkan -- setiap iklan, setiap widget media sosial -- merupakan potensi vektor serangan bagi mereka dengan niat jahat:
Kebijakan Keamanan Konten (CSP) dapat mengurangi risiko yang terkait dengan kedua jenis konten ini dengan Anda kemampuan untuk mengizinkan sumber skrip yang tepercaya secara khusus dan saat ini. Ini adalah langkah utama ke arah yang benar, tetapi perlu dicatat bahwa perlindungan yang ditawarkan oleh sebagian besar direktif CSP adalah biner: sumber dayanya diizinkan, atau tidak. Ada kalanya Anda perlu mengatakan "Saya tidak memastikan saya benar-benar memercayai sumber konten ini, tapi hasilnya sangat bagus! Sematkan tolong, Browser, tapi jangan biarkan itu merusak situs saya."
Hak Istimewa Terendah
Intinya, kami mencari mekanisme yang memungkinkan kami untuk memberikan konten yang hanya menyematkan tingkat kemampuan minimum yang diperlukan untuk melakukan tugasnya. Jika widget tidak perlu memunculkan jendela baru, sehingga menghapus akses ke window.open tidak dapat terluka. Jika layanan tersebut tidak memerlukan Flash, menonaktifkan dukungan plugin seharusnya masalah. Kita akan seaman mungkin jika kita mengikuti prinsip setidaknya hak istimewa, dan memblokir setiap fitur yang tidak secara langsung relevan dengan fungsi yang kita inginkan untuk digunakan. Hasilnya, kita tidak lagi harus mepercayai bahwa beberapa bagian konten tersemat tidak akan memanfaatkan hak istimewa yang tidak boleh digunakannya. Ini tidak akan memiliki akses ke fungsionalitas itu sejak awal.
Elemen iframe
adalah langkah pertama menuju framework yang baik untuk solusi semacam itu.
Memuat beberapa komponen yang tidak tepercaya di iframe
memberikan ukuran pemisahan
antara aplikasi Anda dan konten yang ingin dimuat. Konten berbingkai
tidak akan memiliki akses ke DOM halaman Anda, atau data yang telah Anda simpan secara lokal, juga tidak akan
dapat menggambar ke posisi apa pun di laman; cakupannya
terbatas pada
garis besar {i>frame<i} tersebut. Namun, pemisahan tersebut tidak benar-benar kuat. Halaman yang dimuat
masih memiliki sejumlah opsi untuk perilaku yang mengganggu atau berbahaya: putar otomatis
video, plugin, dan {i>pop-up<i}
adalah awal dari banyak hal.
Atribut sandbox
dari elemen iframe
memberikan apa yang kita butuhkan untuk
memperketat pembatasan konten berbingkai. Kita dapat
menginstruksikan browser untuk memuat konten frame tertentu dengan hak istimewa rendah
keamanan, yang hanya memungkinkan subset kemampuan yang diperlukan untuk melakukan apa pun
perlu dilakukan pekerjaan.
Mengejutkan, tetapi memverifikasi
"Tweet" Twitter adalah contoh fungsi yang bagus yang dapat lebih disematkan dengan aman di situs Anda melalui sandbox. Twitter memungkinkan Anda menyematkan tombol melalui iframe dengan kode berikut:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Untuk mengetahui apa yang dapat kita kunci, mari kita periksa dengan cermat kemampuan apa yang dibutuhkan tombol tersebut. HTML yang dimuat ke dalam {i>frame<i} mengeksekusi sedikit JavaScript dari server Twitter, dan menghasilkan pop-up yang diisi dengan antarmuka {i>tweet<i} saat diklik. Antarmuka itu membutuhkan akses ke cookie untuk mengaitkan tweet ke akun yang benar, dan memerlukan kemampuan untuk mengirimkan formulir tweeting. Cukup begitu saja; {i>frame<i} tidak perlu memuat plugin apa pun, tidak perlu menavigasi jendela tingkat atas, atau salah satu jumlah bit fungsionalitas lainnya. Karena ia tidak membutuhkan hak istimewa tersebut, mari kita hapus dengan melakukan {i> sandbox<i} konten {i>frame<i}.
Sandbox bekerja berdasarkan daftar yang diizinkan. Kita mulai dengan
menghapus semua
izin akses yang diberikan, dan kemudian mengaktifkan kembali
kemampuan individu dengan menambahkan
penanda tertentu ke konfigurasi sandbox. Untuk widget Twitter, kita telah
memutuskan untuk mengaktifkan JavaScript, popup, pengiriman formulir, dan
cookie. Kita dapat melakukannya dengan menambahkan atribut sandbox
ke iframe
dengan
nilai berikut:
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Selesai. Kita telah memberikan {i>frame<i} semua
kemampuan yang dibutuhkan, dan
browser akan membantu menolak akses ke
hak istimewa yang tidak kita
secara eksplisit memberikannya melalui nilai atribut sandbox
.
Kontrol Terperinci atas Kemampuan
Kita melihat beberapa kemungkinan tanda {i>sandbox<i} pada contoh di atas. Sekarang mari kita mendalami cara kerja bagian dalam atribut dengan sedikit lebih detail.
Mengingat iframe dengan atribut sandbox kosong, dokumen berbingkai akan sepenuhnya di-sandbox, sehingga membuatnya menjadi subjek pembatasan berikut:
- JavaScript tidak akan dijalankan dalam dokumen berbingkai. Hal ini tidak hanya mencakup JavaScript yang dimuat secara eksplisit melalui tag skrip, tetapi juga pengendali peristiwa inline dan javascript: URL. Ini juga berarti bahwa konten yang terkandung dalam tag noscript akan ditampilkan, seolah-olah pengguna telah menonaktifkan skrip sendiri.
- Dokumen berbingkai dimuat ke asal yang unik, yang berarti bahwa semua pemeriksaan dari origin yang sama akan gagal; asal yang unik tidak sama dengan asal yang lain, bahkan diri mereka sendiri. Di antara dampak lainnya, ini berarti bahwa dokumen tidak memiliki akses ke data yang disimpan di cookie origin mana pun atau mekanisme penyimpanan lainnya (Penyimpanan DOM, Indexed DB, dll.).
- Dokumen berbingkai tidak dapat membuat jendela atau dialog baru (melalui
window.open
atautarget="_blank"
). - Formulir tidak dapat dikirim.
- Plugin tidak akan dimuat.
- Dokumen berbingkai hanya dapat menavigasi sendiri, bukan induk tingkat teratasnya.
Menyetel
window.top.location
akan menampilkan pengecualian, dan mengklik link dengantarget="_top"
tidak akan berpengaruh. - Fitur yang dipicu secara otomatis (elemen formulir yang difokuskan otomatis, pemutaran otomatis video, dll.) akan diblokir.
- Kunci kursor tidak dapat diperoleh.
- Atribut
seamless
diabaikan diiframes
dalam dokumen berbingkai.
Ini sangat kejam, dan dokumen dimuat ke iframe
dengan sandbox sepenuhnya
memang memiliki risiko yang sangat kecil. Tentu saja, hal itu juga tidak bisa memberikan banyak manfaat: Anda
mungkin bisa menggunakan sandbox penuh untuk beberapa konten statis, tetapi sebagian besar
waktu Anda perlu mengendurkan segalanya.
Dengan pengecualian plugin, masing-masing pembatasan ini dapat dicabut dengan menambahkan tanda ke nilai atribut sandbox. Dokumen dengan sandbox tidak akan pernah menjalankan plugin, karena plugin adalah kode native yang tidak di-sandbox, tetapi yang lainnya adil permainan:
allow-forms
mengizinkan pengiriman formulir.allow-popups
mengizinkan pop-up (shock).allow-pointer-lock
memungkinkan penguncian pointer (secara mengejutkan).allow-same-origin
memungkinkan dokumen mempertahankan asalnya; halaman dimuat darihttps://example.com/
akan mempertahankan akses ke data origin tersebut.allow-scripts
memungkinkan eksekusi JavaScript, dan juga memungkinkan fitur untuk dipicu secara otomatis (karena mudah diterapkan melalui JavaScript).allow-top-navigation
memungkinkan dokumen keluar dari frame dengan menavigasi jendela {i>top-level<i}.
Dengan mengingat hal ini, kita dapat mengevaluasi dengan tepat mengapa kami berakhir dengan satu set tanda sandboxing pada contoh Twitter di atas:
allow-scripts
diperlukan, karena halaman yang dimuat ke dalam frame menjalankan beberapa JavaScript untuk menangani interaksi pengguna.allow-popups
diperlukan, karena tombol akan memunculkan formulir tweet di jendela.allow-forms
diperlukan, karena formulir tweet harus dapat dikirimkan.allow-same-origin
diperlukan, karena cookie twitter.com akan menggunakan tidak dapat diakses, dan pengguna tidak dapat {i>login<i} untuk memposting formulir.
Satu hal penting yang perlu diperhatikan adalah bahwa penanda {i>sandbox<i} yang diterapkan pada sebuah {i>frame<i} juga
berlaku untuk jendela atau
bingkai apa pun yang dibuat di {i>sandbox<i}. Ini berarti kita memiliki
untuk menambahkan allow-forms
ke sandbox frame, meskipun formulir tersebut hanya ada
di jendela tempat {i>frame<i} itu muncul.
Dengan menerapkan atribut sandbox
, widget hanya mendapatkan izin yang diberikan
diperlukan, dan kemampuan seperti plugin, navigasi atas, dan penguncian pointer tetap
diblokir. Kami telah mengurangi risiko penyematan widget tersebut, tanpa efek buruk.
Hal ini menguntungkan bagi semua orang yang peduli.
Pemisahan Hak Istimewa
Melakukan sandbox konten pihak ketiga untuk menjalankan kode yang tidak tepercaya dalam lingkungan dengan hak istimewa rendah sudah jelas menguntungkan. Tapi bagaimana dengan kode Anda sendiri? Kamu percaya pada diri sendiri, kan? Jadi mengapa perlu mengkhawatirkan sandbox?
Saya akan membalikkan pertanyaan itu: jika kode Anda tidak memerlukan plugin, mengapa diberikan akses ke plugin? Paling-paling, ini adalah hak istimewa yang tidak pernah Anda gunakan, paling buruknya itu vektor yang potensial bagi penyerang untuk menginjakkan kakinya di pintu. Kode setiap orang memiliki {i>bugs<i}, dan hampir setiap aplikasi rentan terhadap eksploitasi dengan satu cara atau lainnya. Sandbox kode Anda sendiri berarti bahwa meskipun penyerang berhasil merusak aplikasi Anda, pengguna tersebut tidak akan diberi akses penuh ke asal aplikasi; mereka hanya akan bisa melakukan hal-hal yang bisa dilakukan aplikasi fungsi tersebut. Masih buruk, tapi tidak seburuk biasanya.
Anda dapat mengurangi risiko lebih jauh dengan memecah aplikasi menjadi bagian logis dan {i>sandbox <i}setiap bagian dengan hak istimewa seminimal mungkin. Teknik ini sangat umum dalam kode native: Chrome, misalnya, merusak dirinya sendiri proses browser dengan hak istimewa tinggi yang memiliki akses ke {i>hard drive<i} lokal dan dapat membuat koneksi jaringan, serta banyak proses perender dengan hak istimewa rendah yang melakukan tugas berat dalam mengurai konten yang tidak tepercaya. Perender tidak perlu menyentuh {i>disk<i}, {i>browser<i} akan memberikan semua informasi yang dibutuhkan untuk merender halaman. Bahkan jika peretas yang pintar menemukan cara untuk merusak perender, dia belum terlalu jauh, karena perender tidak dapat melakukan banyak hal menarik sendiri: semua akses hak istimewa tinggi harus diarahkan melalui proses {i>browser<i}. Penyerang perlu menemukan beberapa lubang di bagian sistem yang berbeda menimbulkan kerusakan, yang sangat mengurangi risiko serangan pwnage.
Sandbox eval()
dengan aman
Dengan sandboxing dan
postMessage
API,
kesuksesan model ini cukup mudah
untuk diterapkan di web. Bagian dari
aplikasi Anda dapat berada di iframe
dengan sandbox, dan dokumen induk dapat
perantara komunikasi di antara mereka
dengan memposting pesan dan mendengarkan
yang dihasilkan. Struktur semacam ini memastikan bahwa
eksploitasi di salah satu bagian
aplikasi Anda dapat melakukan kerusakan
seminimal mungkin. Hal ini juga memiliki keuntungan
untuk memaksa Anda untuk
membuat titik integrasi yang jelas, sehingga Anda tahu persis di mana Anda harus
berhati-hati dalam memvalidasi input dan output. Mari kita lihat contoh mainan,
untuk melihat cara kerjanya.
Evalbox adalah aplikasi yang menarik yang mengambil {i>string<i}, dan mengevaluasinya sebagai JavaScript. Wah, benar kan? Apa saja yang telah Anda tunggu-tunggu selama bertahun-tahun. Ini adalah serangan yang cukup berbahaya aplikasi, tentu saja, karena mengizinkan JavaScript arbitrer untuk dieksekusi berarti bahwa setiap dan semua data yang ditawarkan origin siap diperebutkan. Kita akan memitigasi risiko {i>Bad ThingsTM<i} terjadi dengan memastikan bahwa kode dieksekusi di dalam {i>sandbox<i}, yang membuatnya sedikit lebih aman. Kita akan mempelajari kode dari ke dalam, dimulai dengan isi {i>frame<i}:
<!-- frame.html -->
<!DOCTYPE html>
<html>
<head>
<title>Evalbox's Frame</title>
<script>
window.addEventListener('message', function (e) {
var mainWindow = e.source;
var result = '';
try {
result = eval(e.data);
} catch (e) {
result = 'eval() threw an exception.';
}
mainWindow.postMessage(result, event.origin);
});
</script>
</head>
</html>
Di dalam frame, kita memiliki dokumen minimal yang hanya memproses pesan
dari induknya dengan mengaitkan ke peristiwa message
dari objek window
.
Setiap kali induk mengeksekusi postMessage pada konten iframe, peristiwa ini akan
akan dipicu, sehingga memberi kita akses ke
string yang diinginkan oleh induk kita
mengeksekusi.
Di pengendali, kita mengambil atribut source
peristiwa, yang merupakan induk
jendela. Kita akan menggunakannya untuk mengirimkan hasil
kerja keras kita kembali setelah kita
selesai. Kemudian kita akan melakukan bagian pekerjaan yang sulit, dengan meneruskan data yang telah diberikan ke
eval()
. Panggilan ini telah ditutup dalam blok coba, sebagai operasi yang diblokir
di dalam iframe
dengan sandbox akan sering menghasilkan pengecualian DOM; kita akan menangkap
dan melaporkan pesan {i>error<i}. Terakhir, kita posting hasil
kembali ke jendela induk. Ini adalah hal yang cukup sederhana.
Induknya juga tidak rumit. Kita akan membuat UI kecil dengan textarea
untuk kode, dan button
untuk eksekusi, lalu kita akan menarik frame.html
melalui
iframe
dalam sandbox, hanya memungkinkan eksekusi skrip:
<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
id='sandboxed'
src='frame.html'></iframe>
Sekarang kita akan menyiapkan semuanya untuk dieksekusi. Pertama, kita akan mendengarkan
respons dari
iframe
dan alert()
kepada pengguna. Diduga aplikasi nyata
akan melakukan sesuatu yang tidak terlalu mengganggu:
window.addEventListener('message',
function (e) {
// Sandboxed iframes which lack the 'allow-same-origin'
// header have "null" rather than a valid origin. This means you still
// have to be careful about accepting data via the messaging API you
// create. Check that source, and validate those inputs!
var frame = document.getElementById('sandboxed');
if (e.origin === "null" && e.source === frame.contentWindow)
alert('Result: ' + e.data);
});
Berikutnya, kita akan menghubungkan pengendali peristiwa untuk mengklik button
. Saat pengguna
klik, kita akan mengambil konten textarea
saat ini, dan meneruskannya ke
{i>frame<i} untuk dieksekusi:
function evaluate() {
var frame = document.getElementById('sandboxed');
var code = document.getElementById('code').value;
// Note that we're sending the message to "*", rather than some specific
// origin. Sandboxed iframes which lack the 'allow-same-origin' header
// don't have an origin which you can target: you'll have to send to any
// origin, which might alow some esoteric attacks. Validate your output!
frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);
Mudah, kan? Kita telah membuat API evaluasi yang sangat sederhana, dan dapat yakin bahwa kode yang dievaluasi tidak memiliki akses ke informasi sensitif seperti cookie atau penyimpanan DOM. Demikian juga, kode yang dievaluasi tidak dapat memuat plugin, memunculkan jendela baru, atau sejumlah aktivitas mengganggu atau jahat lainnya.
Anda dapat melakukan hal yang sama untuk kode Anda sendiri dengan memecah aplikasi monolitik menjadi komponen dengan tujuan tunggal. Masing-masing dapat digabungkan dalam API pesan sederhana, cukup seperti yang telah kita tulis di atas. Jendela induk dengan hak istimewa tinggi dapat bertindak sebagai {i>controller<i} dan operator, mengirim pesan ke modul khusus yang masing-masing memiliki hak istimewa yang paling sedikit untuk melakukan pekerjaan, mendengarkan hasil, dan memastikan bahwa setiap modul hanya diberi informasi yang dibutuhkan.
Namun, perlu diperhatikan bahwa Anda harus sangat berhati-hati saat menangani konten berbingkai
yang berasal dari asal yang
sama dengan induknya. Jika halaman di
https://example.com/
membingkai halaman lain di asal yang sama dengan sandbox
yang menyertakan flag allow-same-origin dan allow-scripts, lalu
halaman yang berbingkai bisa menjangkau induk, dan menghapus atribut {i>sandbox<i}
sepenuhnya.
Bermain di sandbox
Sandboxing kini tersedia di berbagai browser: Firefox 17+,
IE10+, dan Chrome pada saat penulisan (kaniuse, tentu saja, memiliki
tabel dukungan). Menerapkan sandbox
ke iframes
yang Anda sertakan memungkinkan Anda memberikan hak istimewa tertentu kepada
konten yang ditampilkan, hanya hak istimewa yang diperlukan untuk
konten berfungsi dengan baik. Hal ini memberi Anda kesempatan
untuk mengurangi risiko
yang terkait dengan penyertaan konten pihak ketiga, melebihi apa yang
dapat dilakukan dengan Content Security
Kebijakan kami.
Selain itu, {i>sandbox<i} adalah teknik ampuh untuk mengurangi risiko bahwa penyerang akan dapat mengeksploitasi lubang di kode Anda sendiri. Dengan memisahkan aplikasi monolitik ke dalam satu set layanan dalam sandbox, masing-masing bertanggung jawab untuk sepotong kecil fungsionalitas mandiri, penyerang akan dipaksa untuk tidak hanya mengganggu {i>frame<i} tertentu konten, tetapi juga pengontrolnya. Itu adalah tugas yang jauh lebih sulit, terutama karena {i> controller<i} dapat sangat berkurang ke dalam cakupan. Anda dapat menghabiskan upaya terkait keamanan untuk mengaudit kode itu jika Anda minta bantuan ke browser untuk sisanya.
Ini tidak berarti bahwa {i>sandbox<i} adalah solusi lengkap untuk masalah keamanan di internet. Cara ini menawarkan pertahanan yang mendalam, kecuali jika Anda memiliki pengguna Anda Anda belum dapat mengandalkan dukungan browser untuk semua pengguna Anda (jika Anda mengontrol, klien pengguna Anda -- lingkungan perusahaan, misalnya -- hore!). Suatu hari... tetapi untuk saat ini, {i>sandbox<i} adalah lapisan lain dari perlindungan untuk memperkuat pertahanan Anda, bukanlah pertahanan lengkap yang dapat Anda andalkan. Namun, lapisan sangat baik. Saya sarankan untuk memanfaatkan satu.
Bacaan Lebih Lanjut
"Pemisahan Hak Istimewa dalam Aplikasi HTML5" adalah makalah menarik yang bekerja melalui desain kerangka kerja kecil, dan penerapannya ke tiga aplikasi HTML5 yang ada.
Sandboxing dapat menjadi lebih fleksibel jika dikombinasikan dengan dua iframe baru lainnya atribut:
srcdoc
, danseamless
. Metode pertama memungkinkan Anda mengisi {i>frame<i} dengan konten tanpa overhead permintaan HTTP, dan yang kedua memungkinkan gaya untuk mengalir ke konten yang dibingkai. Keduanya memiliki dukungan browser yang cukup buruk saat ini (Chrome dan WebKit malam). tetapi akan menjadi kombinasi yang menarik di masa mendatang. Anda bisa, misalnya, komentar sandbox di artikel melalui kode berikut:<iframe sandbox seamless srcdoc="<p>This is a user's comment! It can't execute script! Hooray for safety!</p>"></iframe>