Fungsi eval adalah cara yang kuat dan mudah untuk menghasilkan kode secara dinamis, jadi apa peringatannya?
Fungsi eval adalah cara yang kuat dan mudah untuk menghasilkan kode secara dinamis, jadi apa peringatannya?
Jawaban:
Penggunaan eval yang tidak tepat akan membuka kode Anda untuk serangan injeksi
Debugging bisa lebih menantang (tidak ada nomor baris, dll.)
kode eval'd mengeksekusi lebih lambat (tidak ada kesempatan untuk mengkompilasi / cache kode eval'd)
Sunting: Seperti yang ditunjukkan oleh @Jeff Walden dalam komentar, # 3 kurang benar hari ini dari pada 2008. Namun, sementara beberapa caching skrip yang dikompilasi dapat terjadi, ini hanya akan terbatas pada skrip yang dievaluasi berulang tanpa modifikasi. Skenario yang lebih mungkin adalah Anda mengevaluasi skrip yang telah mengalami sedikit modifikasi setiap kali dan karenanya tidak dapat di-cache. Anggap saja beberapa kode dijalankan lebih lambat.
badHackerGuy'); doMaliciousThings();
dan jika Anda mengambil nama pengguna saya, menggabungkannya menjadi beberapa skrip dan mengevalasinya di browser orang lain maka saya dapat menjalankan javascript apa pun yang saya inginkan di mesin mereka (misalnya memaksa mereka untuk memberi +1 pada posting saya, memposting data mereka ke server saya, dll.)
debugger;
pernyataan ke kode sumber Anda. Ini akan menghentikan eksekusi program Anda pada baris itu. Kemudian setelah itu Anda dapat menambahkan debug breakpoint seperti itu hanya file JS lainnya.
eval tidak selalu jahat. Ada saat-saat di mana itu sangat tepat.
Namun, eval saat ini dan secara historis banyak digunakan oleh orang-orang yang tidak tahu apa yang mereka lakukan. Itu termasuk orang yang menulis tutorial JavaScript, sayangnya, dan dalam beberapa kasus ini memang dapat memiliki konsekuensi keamanan - atau, lebih sering, bug sederhana. Jadi semakin banyak yang bisa kita lakukan untuk melemparkan tanda tanya di atas eval, semakin baik. Setiap kali Anda menggunakan eval, Anda perlu melakukan kewarasan-periksa apa yang Anda lakukan, karena kemungkinan besar Anda bisa melakukannya dengan cara yang lebih baik, lebih aman, lebih bersih.
Untuk memberikan contoh yang terlalu umum, untuk mengatur warna elemen dengan id yang disimpan dalam variabel 'kentang':
eval('document.' + potato + '.style.color = "red"');
Jika penulis dari jenis kode di atas memiliki petunjuk tentang dasar-dasar cara kerja objek JavaScript, mereka akan menyadari bahwa tanda kurung siku dapat digunakan alih-alih nama-huruf literal, meniadakan perlunya eval:
document[potato].style.color = 'red';
... yang jauh lebih mudah dibaca dan juga kurang berpotensi buggy.
(Tapi kemudian, seseorang yang benar-benar tahu apa yang mereka lakukan akan berkata:
document.getElementById(potato).style.color = 'red';
yang lebih dapat diandalkan daripada trik lama cerdik mengakses elemen DOM langsung dari objek dokumen.)
JSON.parse()
bukan eval()
untuk JSON?
Saya percaya itu karena dapat menjalankan fungsi JavaScript apa pun dari string. Menggunakannya memudahkan orang untuk menyuntikkan kode jahat ke dalam aplikasi.
Dua poin muncul dalam pikiran:
Keamanan (tetapi selama Anda menghasilkan string untuk dievaluasi sendiri, ini mungkin bukan masalah)
Kinerja: sampai kode yang dieksekusi tidak diketahui, tidak dapat dioptimalkan. (tentang javascript dan kinerja, tentu saja presentasi Steve Yegge )
eval
dan kemudian, sepotong kecil kode itu berjalan di browser B pengguna
Melewati input pengguna ke eval () adalah risiko keamanan, tetapi juga setiap permintaan eval () menciptakan instance baru dari penerjemah JavaScript. Ini bisa menjadi sumber daya babi.
Terutama, jauh lebih sulit untuk mempertahankan dan men-debug. Itu seperti goto
. Anda dapat menggunakannya, tetapi itu membuat lebih sulit untuk menemukan masalah dan lebih sulit pada orang-orang yang mungkin perlu melakukan perubahan nanti.
Satu hal yang perlu diingat adalah bahwa Anda sering dapat menggunakan eval () untuk mengeksekusi kode di lingkungan yang dibatasi - situs jejaring sosial yang memblokir fungsi JavaScript tertentu terkadang dapat dibodohi dengan memecahnya dalam blok eval -
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
Jadi, jika Anda ingin menjalankan beberapa kode JavaScript yang mungkin tidak diizinkan ( Myspace , saya melihat Anda ...) maka eval () dapat menjadi trik yang bermanfaat.
Namun, untuk semua alasan yang disebutkan di atas, Anda tidak boleh menggunakannya untuk kode Anda sendiri, di mana Anda memiliki kontrol penuh - itu tidak perlu, dan lebih baik diturunkan ke rak 'hacks JavaScript rumit'.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
Kecuali Anda membiarkan eval () konten dinamis (melalui cgi atau input), itu sama aman dan solidnya dengan semua JavaScript lainnya di halaman Anda.
Seiring dengan sisa jawaban, saya tidak berpikir pernyataan eval dapat memiliki minimalisasi lanjutan.
Ini adalah risiko keamanan yang mungkin terjadi, ia memiliki cakupan eksekusi yang berbeda, dan cukup tidak efisien, karena menciptakan lingkungan scripting yang sama sekali baru untuk pelaksanaan kode. Lihat di sini untuk info lebih lanjut: eval .
Ini cukup berguna, dan digunakan dengan moderasi dapat menambah banyak fungsionalitas yang baik.
Kecuali Anda 100% yakin bahwa kode yang dievaluasi berasal dari sumber tepercaya (biasanya aplikasi Anda sendiri) maka itu cara yang pasti untuk mengekspos sistem Anda terhadap serangan skrip lintas situs.
Saya tahu diskusi ini sudah lama, tetapi saya sangat suka pendekatan ini oleh Google dan ingin berbagi perasaan itu dengan orang lain;)
Hal lain adalah bahwa semakin baik Anda mendapatkan semakin Anda mencoba untuk memahami dan akhirnya Anda hanya tidak percaya bahwa ada sesuatu yang baik atau buruk hanya karena seseorang berkata begitu :) Ini adalah video yang sangat inspirasional yang membantu saya untuk berpikir lebih banyak sendiri :) PRAKTEK YANG BAIK baik, tetapi jangan menggunakannya dengan sembarangan :)
Itu tidak selalu buruk asalkan Anda tahu apa konteksnya Anda menggunakannya.
Jika aplikasi Anda menggunakan eval()
untuk membuat objek dari beberapa JSON yang telah kembali dari XMLHttpRequest ke situs Anda sendiri, dibuat oleh kode sisi server tepercaya Anda, itu mungkin bukan masalah.
Kode JavaScript sisi-klien yang tidak dapat dipercaya tidak dapat melakukan banyak hal. Asalkan hal yang Anda jalankan eval()
berasal dari sumber yang masuk akal, Anda baik-baik saja.
Ini sangat mengurangi tingkat kepercayaan Anda tentang keamanan.
Jika Anda ingin pengguna memasukkan beberapa fungsi logis dan mengevaluasi DAN ATAU maka fungsi JavaScript JavaScript sempurna. Saya dapat menerima dua string dan eval(uate) string1 === string2
, dll.
Jika Anda melihat penggunaan eval () dalam kode Anda, ingat mantra "eval () adalah jahat."
Fungsi ini mengambil string arbitrer dan menjalankannya sebagai kode JavaScript. Ketika kode tersebut diketahui sebelumnya (tidak ditentukan saat runtime), tidak ada alasan untuk menggunakan eval (). Jika kode dihasilkan secara dinamis saat runtime, sering kali ada cara yang lebih baik untuk mencapai tujuan tanpa eval (). Misalnya, hanya menggunakan notasi braket persegi untuk mengakses properti dinamis lebih baik dan sederhana:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
Penggunaan eval()
juga memiliki implikasi keamanan, karena Anda mungkin menjalankan kode (misalnya berasal dari jaringan) yang telah dirusak. Ini adalah antipattern yang umum ketika berhadapan dengan respons JSON dari permintaan Ajax. Dalam kasus tersebut, lebih baik menggunakan metode bawaan browser untuk mem-parse respons JSON untuk memastikan itu aman dan valid. Untuk peramban yang tidak mendukungJSON.parse()
secara asli, Anda dapat menggunakan perpustakaan dari JSON.org.
Ini juga penting untuk diingat bahwa string yang lewat untuk setInterval()
, setTimeout()
, dan Function()
konstruktor adalah, untuk sebagian besar, mirip dengan menggunakan eval()
dan karena itu harus dihindari.
Di balik layar, JavaScript masih harus mengevaluasi dan mengeksekusi string yang Anda berikan sebagai kode pemrograman:
// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
Menggunakan Function () constructor baru mirip dengan eval () dan harus didekati dengan hati-hati. Ini bisa menjadi konstruksi yang kuat tetapi sering disalahgunakan. Jika Anda benar-benar harus menggunakaneval()
, Anda dapat mempertimbangkan menggunakan Function () baru sebagai gantinya.
Ada sedikit manfaat potensial karena kode yang dievaluasi dalam Function () baru akan berjalan dalam lingkup fungsi lokal, sehingga variabel apa pun yang didefinisikan dengan var dalam kode yang dievaluasi tidak akan menjadi global secara otomatis.
Cara lain untuk mencegah global otomatis adalah dengan membungkus
eval()
panggilan menjadi fungsi langsung.
Selain kemungkinan masalah keamanan jika Anda mengeksekusi kode yang dikirimkan pengguna, sebagian besar waktu ada cara yang lebih baik yang tidak melibatkan penguraian ulang kode setiap kali dieksekusi. Fungsi anonim atau properti objek dapat menggantikan sebagian besar penggunaan eval dan jauh lebih aman dan lebih cepat.
Ini adalah salah satu artikel bagus yang membahas eval dan bagaimana ini bukan kejahatan: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
Saya tidak mengatakan Anda harus kehabisan dan mulai menggunakan eval () di mana-mana. Bahkan, ada sangat sedikit kasus penggunaan yang baik untuk menjalankan eval () sama sekali. Jelas ada kekhawatiran dengan kejelasan kode, kemampuan debug, dan tentu saja kinerja yang tidak boleh diabaikan. Tetapi Anda tidak perlu takut untuk menggunakannya ketika Anda memiliki kasus di mana eval () masuk akal. Cobalah untuk tidak menggunakannya terlebih dahulu, tetapi jangan biarkan orang lain membuat Anda berpikir kode Anda lebih rapuh atau kurang aman ketika eval () digunakan dengan tepat.
eval () sangat kuat dan dapat digunakan untuk mengeksekusi pernyataan JS atau mengevaluasi ekspresi. Tetapi pertanyaannya bukan tentang penggunaan eval () tetapi katakan saja bagaimana string yang Anda jalankan dengan eval () dipengaruhi oleh pihak jahat. Pada akhirnya Anda akan menjalankan kode jahat. Dengan kekuatan muncul tanggung jawab besar. Jadi gunakan dengan bijak Anda menggunakannya. Ini tidak terkait banyak dengan fungsi eval () tetapi artikel ini memiliki informasi yang cukup bagus: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Jika Anda mencari dasar-dasar eval () lihat di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Mesin JavaScript memiliki sejumlah optimasi kinerja yang dilakukan selama fase kompilasi. Beberapa di antaranya bermuara pada dasarnya dapat menganalisis kode secara statis seperti lexes, dan menentukan di mana semua variabel dan deklarasi fungsi berada, sehingga dibutuhkan sedikit usaha untuk menyelesaikan pengidentifikasi selama eksekusi.
Tetapi jika Mesin menemukan eval (..) dalam kode, itu pada dasarnya harus mengasumsikan bahwa semua kesadarannya tentang lokasi pengidentifikasi mungkin tidak valid, karena ia tidak dapat mengetahui pada waktu lexing kode apa yang mungkin Anda lewati untuk eval (..) untuk memodifikasi lingkup leksikal, atau konten objek yang dapat Anda gunakan untuk membuat lingkup leksikal baru untuk dikonsultasikan.
Dengan kata lain, dalam pengertian pesimistis, sebagian besar optimisasi yang dibuatnya tidak ada gunanya jika eval (..) ada, jadi sama sekali tidak melakukan optimasi sama sekali.
Ini menjelaskan semuanya.
Referensi:
https://github.com/getify/Anda-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval
https://github.com/getify/Anda-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance
Itu tidak selalu merupakan ide yang buruk. Ambil contoh, 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 hyperscript yang kemudian digunakan oleh virtual-dom. 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
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
Untuk ini
(function (state) {
var Runtime = Hyperbars.Runtime;
var context = state;
return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
return [h('span', {}, [options['@index'], context])]
})])
}.bind({}))
Kinerja eval()
bukan masalah dalam situasi seperti ini 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 .
Saya akan mengatakan bahwa tidak masalah jika Anda menggunakan eval()
javascript yang dijalankan di browser. * (Peringatan)
Semua browser modern memiliki konsol pengembang tempat Anda dapat menjalankan javascript sewenang-wenang dan pengembang semi-cerdas dapat melihat sumber JS Anda dan memasukkan bit apa pun yang mereka perlukan ke konsol dev untuk melakukan apa yang mereka inginkan.
* Selama titik akhir server Anda memiliki validasi & sanitasi yang benar dari nilai yang diberikan pengguna, tidak masalah apa yang diuraikan dan dievaluasi di javascript sisi klien Anda.
Jika Anda bertanya apakah itu cocok untuk digunakan eval()
dalam PHP, jawabannya adalah TIDAK , kecuali jika Anda memasukkan daftar putih nilai apa pun yang dapat diteruskan ke pernyataan eval Anda.
Saya tidak akan mencoba untuk menyangkal apa pun yang dikatakan sebelumnya, tetapi saya akan menawarkan penggunaan eval ini () yang (sejauh yang saya tahu) tidak dapat dilakukan dengan cara lain. Mungkin ada cara lain untuk kode ini, dan mungkin cara untuk mengoptimalkannya, tetapi ini dilakukan dengan tangan dan tanpa bel dan peluit demi kejelasan untuk menggambarkan penggunaan eval yang benar-benar tidak memiliki alternatif lain. Yaitu: nama objek yang dibuat secara dinamis (atau lebih akurat) secara terprogram (sebagai lawan dari nilai).
//Place this in a common/global JS lib:
var NS = function(namespace){
var namespaceParts = String(namespace).split(".");
var namespaceToTest = "";
for(var i = 0; i < namespaceParts.length; i++){
if(i === 0){
namespaceToTest = namespaceParts[i];
}
else{
namespaceToTest = namespaceToTest + "." + namespaceParts[i];
}
if(eval('typeof ' + namespaceToTest) === "undefined"){
eval(namespaceToTest + ' = {}');
}
}
return eval(namespace);
}
//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
//Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
//Code goes here
//this.MyOtherMethod("foo")); // => "foo"
return true;
}
//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);
EDIT: omong-omong, saya tidak akan menyarankan (untuk semua alasan keamanan menunjukkan sebelumnya) bahwa Anda mendasarkan nama objek Anda pada input pengguna. Saya tidak bisa membayangkan alasan bagus Anda ingin melakukan itu. Tetap saja, kupikir aku akan menunjukkan bahwa itu bukan ide yang bagus :)
namespaceToTest[namespaceParts[i]]
, tidak perlu untuk eval di sini, jadi satu if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
-satunya perbedaan untukelse namespaceToTest = namespaceToTest[namespaceParts[i]];
Pengumpulan sampah
Pengumpulan sampah browser tidak tahu apakah kode yang dievaluasi dapat dihapus dari memori sehingga hanya menyimpannya hingga halaman dimuat ulang. Tidak terlalu buruk jika pengguna Anda tidak lama berada di halaman Anda, tetapi ini bisa menjadi masalah bagi webapp.
Berikut ini skrip untuk menunjukkan masalahnya
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
Sesuatu yang sederhana seperti kode di atas menyebabkan sejumlah kecil memori disimpan hingga aplikasi mati. Ini lebih buruk ketika skrip evaled adalah fungsi raksasa, dan disebut interval.