Saya sedang menulis beberapa kode JavaScript untuk mem-parsing fungsi yang dimasukkan pengguna (untuk fungsionalitas seperti spreadsheet). Setelah mem-parsing rumus, saya bisa mengubahnya menjadi JavaScript dan menjalankannya eval()
untuk menghasilkan hasilnya.
Namun, saya selalu menghindari menggunakan eval()
jika saya bisa menghindarinya karena itu jahat (dan, benar atau salah, saya selalu berpikir itu bahkan lebih jahat dalam JavaScript, karena kode yang akan dievaluasi mungkin diubah oleh pengguna ).
Jadi, kapan boleh menggunakannya?
javascript
coding-style
eval
Richard Turner
sumber
sumber
Jawaban:
Saya ingin meluangkan waktu untuk membahas alasan pertanyaan Anda - bahwa eval () adalah " jahat ". Kata " jahat ", seperti yang digunakan oleh orang-orang bahasa pemrograman, biasanya berarti "berbahaya", atau lebih tepatnya "dapat menyebabkan banyak kerusakan dengan perintah yang tampak sederhana". Jadi, kapan boleh menggunakan sesuatu yang berbahaya? Ketika Anda tahu apa bahayanya, dan kapan Anda mengambil tindakan pencegahan yang sesuai.
Untuk intinya, mari kita lihat bahaya dalam penggunaan eval (). Mungkin ada banyak bahaya kecil yang tersembunyi seperti halnya yang lain, tetapi dua risiko besar - alasan mengapa eval () dianggap jahat - adalah kinerja dan injeksi kode.
Aktif ke kasus spesifik Anda. Dari apa yang saya mengerti, Anda menghasilkan string sendiri, jadi dengan asumsi Anda berhati-hati untuk tidak membiarkan string seperti "rm -rf sesuatu-penting" dihasilkan, tidak ada risiko injeksi kode (tapi harap diingat, itu sangat sangat sulit untuk memastikan ini dalam kasus umum). Juga, jika Anda menjalankan di browser maka injeksi kode adalah risiko yang cukup kecil, saya percaya.
Adapun kinerja, Anda harus mempertimbangkan bahwa terhadap kemudahan pengkodean. Menurut pendapat saya, jika Anda menguraikan rumus, Anda sebaiknya menghitung hasilnya selama penguraian daripada menjalankan pengurai lain (yang ada di dalam eval ()). Tetapi mungkin lebih mudah untuk menggunakan kode eval (), dan kinerja mungkin tidak akan terlihat. Sepertinya eval () dalam hal ini tidak lebih jahat daripada fungsi lain yang mungkin bisa menghemat waktu Anda.
sumber
eval()
dari server Anda, mereka juga bisa mengubah sumber halaman di tempat pertama, dan juga mengendalikan informasi pengguna. . . Saya tidak melihat perbedaannya.eval()
itu tidak jahat. Atau, jika benar, itu jahat dengan cara yang sama bahwa refleksi, file / jaringan I / O, threading, dan IPC adalah "jahat" dalam bahasa lain.Jika, untuk tujuan Anda ,
eval()
lebih cepat dari interpretasi manual, atau membuat kode Anda lebih sederhana, atau lebih jelas ... maka Anda harus menggunakannya. Jika tidak, maka Anda seharusnya tidak. Sederhana seperti itu.sumber
Saat Anda mempercayai sumbernya.
Dalam kasus JSON, lebih atau kurang sulit untuk merusak sumbernya, karena berasal dari server web yang Anda kontrol. Selama JSON itu sendiri tidak mengandung data yang diunggah pengguna, tidak ada kelemahan utama untuk menggunakan eval.
Dalam semua kasus lain saya akan berusaha keras untuk memastikan data yang diberikan pengguna sesuai dengan aturan saya sebelum mengumpankannya ke eval ().
sumber
eval
juga tidak akan mem-parsing semua string JSON yang benar dengan benar. MisalnyaJSON.parse(' "\u2028" ') === "\u2028"
tetapieval(' "\u2028" ')
memunculkan pengecualian karena U + 2028 adalah baris baru dalam JavaScript tetapi ini bukan baris baru sejauh yang terkait JSON.Mari kita dapatkan orang-orang nyata:
Setiap peramban utama sekarang memiliki konsol bawaan yang dapat digunakan peretas Anda dengan berlimpah untuk menjalankan fungsi apa pun dengan nilai apa pun - mengapa mereka repot-repot menggunakan pernyataan yang benar - bahkan jika mereka bisa?
Jika diperlukan 0,2 detik untuk mengkompilasi 2000 baris JavaScript, berapakah penurunan kinerja saya jika saya mengevaluasi empat baris JSON?
Bahkan penjelasan Crockford untuk 'eval is evil' lemah.
Seperti yang dikatakan Crockford sendiri, "Pernyataan seperti ini cenderung menghasilkan neurosis irasional. Jangan membelinya."
Memahami eval dan mengetahui kapan itu mungkin berguna jauh lebih penting. Misalnya, eval adalah alat yang masuk akal untuk mengevaluasi respons server yang dihasilkan oleh perangkat lunak Anda.
BTW: Prototype.js memanggil eval secara langsung lima kali (termasuk di evalJSON () dan evalResponse ()). jQuery menggunakannya di parseJSON (via Function constructor).
sumber
Saya cenderung mengikuti saran Crockford untuk
eval()
, dan menghindarinya sama sekali. Bahkan cara-cara yang tampaknya membutuhkannya tidak. Misalnya,setTimeout()
memungkinkan Anda untuk melewati fungsi daripada eval.Bahkan jika itu sumber yang tepercaya , saya tidak menggunakannya, karena kode yang dikembalikan oleh JSON mungkin kacau, yang paling baik bisa melakukan sesuatu yang tidak beres, paling buruk, mengekspos sesuatu yang buruk.
sumber
Saya melihat orang menganjurkan untuk tidak menggunakan eval, karena itu jahat , tapi saya melihat orang yang sama menggunakan Function dan setTimeout secara dinamis, jadi mereka menggunakan eval di bawah tenda : D
BTW, jika kotak pasir Anda tidak cukup yakin (misalnya, jika Anda bekerja di situs yang memungkinkan injeksi kode) eval adalah yang terakhir dari masalah Anda. Aturan dasar keamanan adalah bahwa semua input adalah jahat, tetapi dalam kasus JavaScript bahkan JavaScript itu sendiri bisa jahat, karena dalam JavaScript Anda dapat menimpa fungsi apa pun dan Anda tidak bisa memastikan Anda menggunakan yang asli, jadi, jika kode berbahaya dimulai sebelum Anda, Anda tidak dapat mempercayai fungsi bawaan JavaScript apa pun: D
Sekarang epilog untuk posting ini adalah:
Jika Anda BENAR - BENAR membutuhkannya (80% dari waktu eval TIDAK diperlukan) dan Anda yakin dengan apa yang Anda lakukan, cukup gunakan eval (atau Fungsi yang lebih baik;)), penutupan dan OOP mencakup 80/90% dari kasus di mana eval dapat diganti menggunakan jenis logika lain, sisanya adalah kode yang dihasilkan secara dinamis (misalnya, jika Anda sedang menulis penerjemah) dan seperti yang Anda katakan mengevaluasi JSON (di sini Anda dapat menggunakan evaluasi aman Crockford;))
sumber
Eval adalah komplementer untuk kompilasi yang digunakan dalam templating kode. Dengan template saya maksudkan bahwa Anda menulis generator template yang disederhanakan yang menghasilkan kode template yang berguna yang meningkatkan kecepatan pengembangan.
Saya telah menulis sebuah kerangka kerja, di mana pengembang tidak menggunakan EVAL, tetapi mereka menggunakan kerangka kerja kami dan pada gilirannya kerangka kerja itu harus menggunakan EVAL untuk menghasilkan template.
Kinerja EVAL dapat ditingkatkan dengan menggunakan metode berikut; alih-alih menjalankan skrip, Anda harus mengembalikan fungsi.
Itu harus diatur sebagai
Caching f pasti akan meningkatkan kecepatan.
Chrome juga memungkinkan debugging fungsi-fungsi tersebut dengan sangat mudah.
Mengenai keamanan, menggunakan eval atau tidak tidak akan membuat perbedaan,
Jika keamanan sisi server Anda cukup kuat bagi siapa pun untuk menyerang dari mana saja, Anda tidak perlu khawatir tentang EVAL. Seperti yang saya sebutkan, jika EVAL tidak ada, penyerang memiliki banyak alat untuk meretas ke server Anda terlepas dari kemampuan EVAL browser Anda.
Eval hanya baik untuk menghasilkan beberapa template untuk melakukan pemrosesan string yang kompleks berdasarkan pada sesuatu yang tidak digunakan sebelumnya. Sebagai contoh, saya lebih suka
Sebagai lawan
Seperti nama tampilan saya, yang bisa berasal dari basis data dan yang tidak hardcoded.
sumber
function (first, last) { return last + ' ' + first }
.eval
sebagian besar pengguna lain . Katakanlah Anda memiliki halaman pengaturan, dan itu memungkinkan Anda mengatur bagaimana nama Anda ditampilkan kepada orang lain. Katakan juga Anda tidak berpikir jernih ketika Anda menulisnya, jadi kotak pilih Anda memiliki opsi seperti<option value="LastName + ' ' + FirstName">Last First</option>
. Saya membuka alat dev saya, mengubahvalue
opsi menjadialert('PWNED!')
, memilih opsi yang diubah, dan mengirimkan formulir. Sekarang, kapan saja orang lain dapat melihat nama tampilan saya, kode itu berjalan.eval
adalah untuk mengeksekusi kode yang bukan bagian dari skrip yang Anda tulis. Jika Anda tidak membutuhkan kekuatan untuk melakukan itu (dan Anda hampir tidak pernah melakukannya), menghindarieval
membantu menghindarkan seluruh kategori masalah. Itu hal yang baik jika kode sisi server Anda kurang sempurna.Ketika debugging di Chrome (v28.0.1500.72), saya menemukan bahwa variabel tidak terikat dengan penutupan jika mereka tidak digunakan dalam fungsi bersarang yang menghasilkan penutupan. Saya kira, itu optimasi mesin JavaScript.
TETAPI : ketika
eval()
digunakan di dalam fungsi yang menyebabkan penutupan, SEMUA variabel fungsi luar terikat ke penutupan, bahkan jika mereka tidak digunakan sama sekali. Jika seseorang memiliki waktu untuk menguji apakah kebocoran memori dapat dihasilkan oleh itu, silakan beri saya komentar di bawah ini.Ini kode pengujian saya:
Yang ingin saya tunjukkan di sini adalah, bahwa eval () tidak harus merujuk ke
eval()
fungsi asli . Itu semua tergantung pada nama fungsinya . Jadi ketika memanggil aslieval()
dengan nama alias (katakanvar noval = eval;
dan kemudian dalam fungsi batinnoval(expression);
) maka evaluasiexpression
mungkin gagal ketika mengacu pada variabel yang harus menjadi bagian dari penutupan, tetapi sebenarnya tidak.sumber
Microsoft menjelaskan mengapa eval () lambat di peramban mereka di Blog IE , Rekomendasi Kinerja JavaScript + IE Bagian 2: Inefisiensi Kode JavaScript .
sumber
Intinya
Jika Anda membuat atau membersihkan kode Anda
eval
, itu tidak pernah jahat .Sedikit Lebih Detail
eval
Adalah jahat jika berjalan di server menggunakan input yang dikirimkan oleh klien yang tidak dibuat oleh pengembang atau yang tidak dibersihkan oleh pengembang .eval
adalah tidak jahat jika berjalan pada klien, bahkan jika menggunakan input unsanitized dibuat oleh klien .Jelas Anda harus selalu membersihkan input, karena memiliki kontrol atas apa yang dikonsumsi kode Anda.
Pemikiran
Klien dapat menjalankan kode arbitrer apa pun yang mereka inginkan, bahkan jika pengembang tidak mengkodekannya; Ini benar tidak hanya untuk apa yang disuarakan, tetapi panggilan untuk
eval
dirinya sendiri .sumber
Satu-satunya contoh ketika Anda harus menggunakan eval () adalah ketika Anda perlu menjalankan JS dinamis dengan cepat. Saya berbicara tentang JS yang Anda unduh secara tidak sinkron dari server ...
... Dan 9 kali dari 10 Anda dapat dengan mudah menghindari melakukan itu dengan refactoring.
sumber
eval
jarang pilihan yang tepat. Meskipun mungkin ada banyak contoh di mana Anda dapat mencapai apa yang perlu Anda capai dengan menggabungkan skrip bersama-sama dan menjalankannya dengan cepat, Anda biasanya memiliki teknik yang lebih kuat dan dapat dipelihara yang Anda inginkan: notasi asosiasi-array (obj["prop"]
sama denganobj.prop
) , penutupan, teknik berorientasi objek, teknik fungsional - gunakan saja.sumber
Sejauh skrip klien berjalan, saya pikir masalah keamanan adalah titik diperdebatkan. Segala sesuatu yang dimuat ke dalam browser tunduk pada manipulasi dan harus diperlakukan seperti itu. Tidak ada risiko menggunakan pernyataan eval () ketika ada banyak cara yang lebih mudah untuk mengeksekusi kode JavaScript dan / atau memanipulasi objek di DOM, seperti bilah URL di browser Anda.
Jika seseorang ingin memanipulasi DOM mereka, saya katakan berayun pergi. Keamanan untuk mencegah segala jenis serangan harus selalu menjadi tanggung jawab aplikasi server, titik.
Dari sudut pandang pragmatis, tidak ada manfaatnya menggunakan eval () dalam situasi di mana hal-hal dapat dilakukan sebaliknya. Namun, ada kasus khusus di mana eval HARUS digunakan. Ketika demikian, itu pasti bisa dilakukan tanpa risiko meledakkan halaman.
sumber
<head></head>
perlu, bahkan jika kosong?Di sisi server, eval berguna ketika berhadapan dengan skrip eksternal seperti sql atau influxdb atau mongo. Di mana validasi khusus saat runtime dapat dilakukan tanpa menggunakan kembali layanan Anda.
Misalnya layanan pencapaian dengan metadata berikut
Yang kemudian memungkinkan,
Injeksi langsung objek / nilai melalui string literal dalam json, berguna untuk templating teks
Dapat digunakan sebagai pembanding, misalkan kita membuat aturan bagaimana memvalidasi pencarian atau acara di CMS
Kontra dari ini:
Dapat terjadi kesalahan dalam kode dan memecah hal-hal dalam layanan, jika tidak sepenuhnya diuji.
Jika seorang hacker dapat menulis skrip di sistem Anda, maka Anda cukup banyak yang kacau.
Salah satu cara untuk memvalidasi skrip Anda adalah menyimpan hash skrip Anda di tempat yang aman, sehingga Anda dapat memeriksanya sebelum berjalan.
sumber
Saya pikir setiap kasus eval dibenarkan jarang terjadi. Anda lebih mungkin menggunakannya berpikir bahwa itu dibenarkan daripada Anda menggunakannya ketika itu sebenarnya dibenarkan.
Masalah keamanan adalah yang paling terkenal. Tetapi juga perlu diperhatikan bahwa JavaScript menggunakan kompilasi JIT dan ini bekerja sangat buruk dengan eval. Eval agaknya seperti kotak hitam ke kompiler, dan JavaScript harus dapat memprediksi kode sebelumnya (sampai batas tertentu) agar dapat dengan aman dan benar menerapkan optimasi dan pelingkupan kinerja. Dalam beberapa kasus, dampak kinerja bahkan dapat memengaruhi kode lain di luar eval.
Jika Anda ingin tahu lebih banyak: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval
sumber
Tidak apa-apa untuk menggunakannya jika Anda memiliki kontrol penuh atas kode yang diberikan ke
eval
fungsi.sumber
eval
, maka pertanyaan besarnya menjadi, kapan masuk akal untuk itu menjadi string daripada JS nyata?<script async="true" src="...">
. Lihat juga: w3bits.com/async-javascriptHanya selama pengujian, jika memungkinkan. Perhatikan juga bahwa eval () jauh lebih lambat daripada evaluator JSON dll. Khusus lainnya.
sumber
Tidak ada alasan untuk tidak menggunakan eval () selama Anda dapat memastikan bahwa sumber kode berasal dari Anda atau pengguna yang sebenarnya. Meskipun ia dapat memanipulasi apa yang dikirim ke fungsi eval (), itu bukan masalah keamanan, karena ia dapat memanipulasi kode sumber situs web dan karenanya dapat mengubah kode JavaScript itu sendiri.
Jadi ... kapan tidak menggunakan eval ()? Eval () seharusnya tidak digunakan ketika ada kemungkinan pihak ketiga dapat mengubahnya. Seperti mencegat koneksi antara klien dan server Anda (tetapi jika itu masalah, gunakan HTTPS). Anda tidak boleh eval () untuk kode parsing yang ditulis oleh orang lain seperti di forum.
sumber
eval
string yang terdiri dari konten dari satu pengguna dapat memungkinkan pengguna untuk mengeksekusi kode di browser pengguna lain.eval
memilikinya. Itu terjadi setiap saat.eval
. Bagaimana berguna menyalahkan server? Jika ada yang harus disalahkan, itu harus penyerang. Terlepas dari kesalahan, klien yang tidak rentan terhadap XSS meskipun bug di server lebih baik daripada klien yang rentan, semuanya sama.Jika benar-benar dibutuhkan, eval bukanlah kejahatan. Tetapi 99,9% dari penggunaan eval yang saya temukan tidak diperlukan (tidak termasuk barang setTimeout).
Bagi saya kejahatan bukanlah kinerja atau bahkan masalah keamanan (yah, secara tidak langsung itu adalah keduanya). Semua penggunaan eval yang tidak perlu seperti itu menambah neraka pemeliharaan. Alat refactoring terlempar. Mencari kode itu sulit. Efek yang tidak diantisipasi dari evals itu sangat banyak.
sumber
Kapan eval () JavaScript tidak jahat?
Saya selalu berusaha untuk tidak menggunakan eval . Hampir selalu, solusi yang lebih bersih dan terawat tersedia. Eval tidak diperlukan bahkan untuk penguraian JSON . Eval menambah neraka pemeliharaan . Bukan tanpa alasan, itu disukai oleh master seperti Douglas Crockford.
Tapi saya menemukan satu contoh di mana itu harus digunakan:
Ketika Anda perlu menyampaikan ekspresi.
Sebagai contoh, saya memiliki fungsi yang membangun
google.maps.ImageMapType
objek umum untuk saya, tetapi saya harus memberi tahu resepnya, bagaimana seharusnya membangun URL ubin dari parameterzoom
dancoord
:sumber
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Contoh saya menggunakan
eval
: import .Bagaimana biasanya dilakukan.
Tetapi dengan bantuan
eval
dan fungsi pembantu kecil itu mendapatkan tampilan yang jauh lebih baik:importable
mungkin terlihat seperti (versi ini tidak mendukung impor anggota konkret).sumber
.replace(/name/g, name).replace('path', path)
. Jikaname
berisi string"path"
maka Anda bisa mendapatkan kejutan.components
adalah kemungkinan bau kode; refactoring kode Anda mungkin menghilangkan 'masalah' sama sekali. Solusi Anda saat ini hanyalah gula sintaksis. Jika Anda bersikeras melakukan itu, maka saya akan merekomendasikan untuk menulis preprocessor Anda sendiri, yang akan dieksekusi sebelum penyebaran. Itu haruseval
dijauhkan dari kode produksi.Eval tidak jahat, hanya disalahgunakan.
Jika Anda membuat kode yang masuk ke dalamnya atau bisa memercayainya, tidak apa-apa. Orang-orang terus berbicara tentang bagaimana input pengguna tidak masalah dengan eval. Yah semacam ~
Jika ada input pengguna yang masuk ke server, maka kembali ke klien, dan kode itu digunakan dalam eval tanpa disanitasi. Selamat, Anda telah membuka kotak pandora untuk mengirim data pengguna kepada siapa pun.
Tergantung di mana eval berada, banyak situs web menggunakan SPA, dan eval dapat membuatnya lebih mudah bagi pengguna untuk mengakses internal aplikasi yang kalau tidak pasti tidak mudah. Sekarang mereka dapat membuat ekstensi peramban palsu yang dapat merekam ke eval itu dan mencuri data lagi.
Hanya harus mencari tahu apa gunanya Anda menggunakan eval. Membuat kode tidak terlalu ideal ketika Anda bisa membuat metode untuk melakukan hal semacam itu, menggunakan objek, atau sejenisnya.
Sekarang contoh yang bagus untuk menggunakan eval. Server Anda sedang membaca file kesombongan yang telah Anda buat. Banyak params URL dibuat dalam format
{myParam}
. Jadi Anda ingin membaca URL dan kemudian mengonversinya menjadi string template tanpa harus melakukan penggantian yang rumit karena Anda memiliki banyak titik akhir. Jadi, Anda dapat melakukan sesuatu seperti ini. Perhatikan ini adalah contoh yang sangat sederhana.sumber
Pembuatan kode. Baru-baru ini saya menulis sebuah perpustakaan bernama Hyperbars yang menjembatani kesenjangan antara virtual-dom dan setang . Ini dilakukan dengan mem- parsing templat templat dan mengubahnya menjadi hiperscript . Hyperscript dibuat sebagai string terlebih dahulu dan sebelum mengembalikannya,
eval()
untuk mengubahnya menjadi kode yang dapat dieksekusi. Saya telah menemukaneval()
dalam situasi khusus ini kebalikan dari kejahatan.Pada dasarnya dari
Untuk ini
Kinerja
eval()
bukan masalah dalam situasi seperti ini juga karena Anda hanya perlu menafsirkan string yang dihasilkan sekali dan kemudian menggunakan kembali hasil yang dapat dieksekusi berkali-kali.Anda dapat melihat bagaimana pembuatan kode tercapai jika Anda penasaran di sini .
sumber
eval
adalah petunjuk bahwa beberapa tanggung jawab yang termasuk dalam waktu kompilasi, telah pindah ke runtime.Keyakinan saya adalah bahwa eval adalah fungsi yang sangat kuat untuk aplikasi web sisi klien dan aman ... Sama amannya dengan JavaScript, padahal sebenarnya tidak. :-) Masalah keamanan pada dasarnya adalah masalah sisi server karena, sekarang, dengan alat seperti Firebug, Anda dapat menyerang aplikasi JavaScript apa pun.
sumber
eval
kebutuhan harus diamankan terhadap serangan XSS, yang tidak selalu mudah untuk diperbaiki.Eval berguna untuk pembuatan kode ketika Anda tidak memiliki makro.
Sebagai contoh (bodoh), jika Anda menulis kompiler Brainfuck , Anda mungkin ingin membangun fungsi yang melakukan urutan instruksi sebagai string, dan mengevaluasi untuk mengembalikan fungsi.
sumber
eval
.eval
, ketika alternatif ( Fungsi ) lebih cepat ( seperti yang dijelaskan dalam MDN ) dan lebih dapat diandalkan (mencegah bug yang tidak dapat diprediksi dengan isolasi yang lebih baik antara kode yang dihasilkan dan kode 'suportif' lainnya pada halaman web yang sama).Saat Anda menguraikan struktur JSON dengan fungsi parse (misalnya, jQuery.parseJSON), ia mengharapkan struktur sempurna dari file JSON (setiap nama properti menggunakan tanda kutip ganda). Namun, JavaScript lebih fleksibel. Oleh karena itu, Anda dapat menggunakan eval () untuk menghindarinya.
sumber
eval
, esp. saat mendapatkan data JSON dari sumber pihak ketiga. Lihat JSON.Stringify tanpa kutipan pada properti? untuk pendekatan yang benar untuk mem-parsing "JSON tanpa mengutip nama kunci".string
dan mendefinisikanstring
sebagai urutan nol atau lebih karakter Unicode, dibungkus dengan tanda kutip ganda, menggunakan backslash escapes.eval
. Dalam aplikasi web multi-penyewa serius apa pun, dengan puluhan pengembang bekerja pada basis kode yang sama, ini tidak dapat diterima.