Kapan eval () JavaScript tidak jahat?

263

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?

Richard Turner
sumber
5
Kebanyakan pustaka JSON tidak, pada kenyataannya, menggunakan eval di bawah tenda, tepatnya untuk melindungi terhadap risiko keamanan.
Sean McMillan
11
@Sean - Baik JQuery dan Prototype use eval (JQuery menggunakannya via Function baru)
plodder
5
@plodder - Di mana Anda mendapatkan info? jQuery telah menggunakan JSON.parse () asli sejak 1,4 (jauh kembali pada 1/2010)! Lihat sendiri: code.jquery.com/jquery-1.4.js
ken
3
"Jelas seseorang harus menggunakan eval () untuk mem-parse JSON" - ini tidak benar, sebaliknya - orang tidak boleh menggunakan eval untuk mem-parse JSON! Gunakan skrip json2.js Douglas Crockfords (pencipta JSON) dari json.org !
TMS
11
@ Thomas ironi di sana adalah bahwa json2.js menggunakan eval untuk mem-parsing JSON
tobyodavies

Jawaban:

262

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.

  • Performance - eval () menjalankan interpreter / compiler. Jika kode Anda dikompilasi, maka ini adalah hit besar, karena Anda perlu memanggil kompiler yang mungkin berat di tengah run-time. Namun, sebagian besar JavaScript masih merupakan bahasa yang diartikan, yang berarti bahwa memanggil eval () bukan hit kinerja besar dalam kasus umum (tetapi lihat komentar spesifik saya di bawah).
  • Injeksi kode - eval () berpotensi menjalankan serangkaian kode di bawah hak yang lebih tinggi. Sebagai contoh, sebuah program yang berjalan sebagai administrator / root tidak akan pernah ingin mengevaluasi () input pengguna, karena input tersebut berpotensi menjadi "rm -rf / etc / file-penting" atau lebih buruk. Sekali lagi, JavaScript di peramban tidak memiliki masalah itu, karena program tetap berjalan di akun milik pengguna. JavaScript sisi server dapat mengalami masalah itu.

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.

pengguna27476
sumber
78
Anda tidak mengatasi masalah kode yang menggunakan eval karena sulit untuk debug
bobobobo
48
Injeksi Kode adalah masalah yang sangat serius untuk javascript jika Anda sama sekali khawatir tentang data pengguna Anda. Kode yang disuntikkan akan berjalan (di browser) seolah-olah itu berasal dari situs Anda, membiarkannya melakukan segala jenis gangguan yang dapat dilakukan pengguna secara manual. Jika Anda mengizinkan (pihak ketiga) kode untuk memasuki halaman Anda, itu dapat memesan sesuatu atas nama pelanggan Anda, atau mengubah gravatar mereka, atau apa pun yang bisa mereka lakukan melalui situs Anda. Berhati-hatilah. Membiarkan peretas memiliki pelanggan Anda sama buruknya dengan membiarkan mereka memiliki server Anda.
Sean McMillan
71
Jika data berasal dari server Anda dan sesuatu yang Anda, pengembang telah buat, tidak ada salahnya menggunakan eval (). Kerugian sebenarnya adalah percaya semua yang Anda baca. Anda melihat banyak orang mengatakan eval () itu jahat dan mereka tidak tahu mengapa kecuali mereka membacanya di suatu tempat.
Vince Panuccio
42
@Sean McMillan: Saya ingin mempercayai Anda, tetapi jika seseorang akan mencegat dan mengubah javascript ke eval()dari server Anda, mereka juga bisa mengubah sumber halaman di tempat pertama, dan juga mengendalikan informasi pengguna. . . Saya tidak melihat perbedaannya.
Walt W
20
"Suntikan kode - ... Sekali lagi, JavaScript di peramban tidak memiliki masalah itu," & "Juga, jika Anda menjalankan di peramban maka injeksi kode merupakan risiko yang sangat kecil, saya percaya." Apakah Anda menyarankan bahwa injeksi kode di browser tidak menjadi masalah? XSS telah berada di 3 teratas dalam daftar 10 teratas OWASP selama beberapa tahun berjalan.
Mike Samuel
72

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.

Shog9
sumber
5
Salah satu tujuan tersebut mungkin untuk menghasilkan kode yang dioptimalkan yang akan terlalu panjang atau terlalu berulang untuk ditulis dengan tangan. Jenis hal yang, di LISP, akan membutuhkan makro.
wberry
5
Ini adalah saran umum sehingga dapat diterapkan pada setiap blok kode yang ada. Benar-benar tidak menambahkan apa pun pada pertanyaan ini; khususnya, tidak membantu siapa pun yang datang ke sini menentukan apakah penggunaan khusus mereka bermasalah atau tidak.
jpmc26
2
Lebih cepat, lebih sederhana, lebih jelas ... Jawaban ini tidak mencakup implikasi keamanan dengan cukup baik.
Ruud Helderman
55

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 ().

Tomalak
sumber
13
String json harus selalu diuji terhadap tata bahasa json sebelum menggunakannya dalam eval (). Jadi string json "{foo: alert ('XSS')}" tidak akan lulus karena "alert ('XSS')" bukan nilai yang tepat.
Gumbo
3
Nah, gunakan HTTPS, kalau begitu. OTOH: man-in-the-middle bukanlah skenario serangan tipikal untuk aplikasi web variasi taman, sedangkan skrip lintas situs adalah.
Tomalak
7
evaljuga tidak akan mem-parsing semua string JSON yang benar dengan benar. Misalnya JSON.parse(' "\u2028" ') === "\u2028"tetapi eval(' "\u2028" ')memunculkan pengecualian karena U + 2028 adalah baris baru dalam JavaScript tetapi ini bukan baris baru sejauh yang terkait JSON.
Mike Samuel
1
@Justin - jika protokol dikompromikan, well, biasanya memuat halaman awal akan dikirim melalui protokol yang sama, dan kemudian itu adalah titik diperdebatkan karena klien sudah dikompromikan karena mungkin.
antinome
1
Kata @Tomalak dengan indah, saya menyebutkan ini dalam jawaban saya sekarang! Luar biasa!
NiCk Newman
25

Mari kita dapatkan orang-orang nyata:

  1. 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?

  2. 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.

eval is Evil, Fungsi eval adalah fitur yang paling banyak disalahgunakan dari JavaScript. Hindari itu

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).

tukang plodder
sumber
10
JQuery menggunakan fungsi JSON.parse bawaan browser jika tersedia (yang jauh lebih cepat & lebih aman), menggunakan eval hanya sebagai mekanisme fallback. Pernyataan "eval is evil" adalah pedoman yang cukup baik.
jjmontes
30
Re "Setiap browser utama sekarang memiliki konsol bawaan ...". Injeksi kode adalah masalah ketika satu pengguna dapat memasukkan kode yang kemudian dijalankan di browser pengguna lain. Konsol browser tidak dengan sendirinya mengizinkan satu pengguna untuk menjalankan kode di browser pengguna lain sehingga mereka tidak relevan ketika memutuskan apakah perlu dilindungi terhadap injeksi kode.
Mike Samuel
28
"Setiap browser utama sekarang memiliki konsol bawaan ... mengapa mereka repot-repot menggunakan pernyataan asli?" - Anda jauh dari sasaran. Saya sarankan Anda mengedit jawabannya. Kemampuan satu pengguna untuk menyuntikkan kode yang dapat berjalan di browser orang lain adalah masalah utama. Dan di sinilah Anda perlu menjadi benar-benar nyata.
akkishore
5
@akkishore, saya akan menghargai jika Anda datang dengan contoh kehidupan nyata yang mendukung pernyataan Anda yang dinyatakan lebih.
Akash Kava
7
@AkashKava Apa yang Anda gagal sadari, adalah bahwa jika saya mengirim javascript di kotak komentar saya, dan javascript itu membuatnya ke database. Ketika pengguna lain melihat komentar itu (yang saya masukkan javascript), eval akan mengambil javascript itu ketika diberikan, dan mengevaluasinya menggunakan penerjemah, menyebabkan javascript tertanam saya dijalankan di browser pengguna lain. Dengan melakukan ini, saya dapat mem-blle semua jenis informasi. Nama pengguna mereka, id pengguna mereka dalam database, alamat email mereka, dll. Ini bukan jawaban yang sulit, jika Anda memiliki XSS Google, Anda akan melihat dalam 10 detik mengapa itu menjadi masalah.
Kyle Richter
18

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.

setTimeout(function() {
  alert('hi');
}, 1000);

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.

swilliams
sumber
2
Saya berpikir bahwa bug di formatter JSON di sisi server tentu menjadi masalah. Apakah respons dari server tergantung pada jenis teks yang dikirimkan pengguna? Maka Anda harus memperhatikan XSS.
swilliams
3
Jika server web Anda tidak diautentikasi melalui HTTPS, maka Anda bisa mengalami semacam serangan man-in-the-middle di mana host lain mencegat permintaan dan mengirimkan datanya sendiri.
Ben Combee
11
Jika seseorang dapat melakukan serangan man-in-the-middle, ia dapat dengan mudah menyuntikkan apa pun ke skrip Anda.
el.pescado
10
Anda seharusnya tidak bergantung pada kode javascript Anda sama sekali ... Anda tidak bergantung pada apa pun yang berjalan di sisi klien ... Jika seseorang melakukan serangan man-in-the-middle mengapa ia mengacaukan objek json Anda? Dia dapat melayani halaman web yang berbeda untuk Anda dan file js yang berbeda ...
Calmarius
5
Saya pribadi tidak suka argumen "selalu ada cara lain untuk melakukannya." Misalnya, Anda juga bisa mengatakan selalu ada cara untuk menghindari pemrograman berorientasi objek. Itu tidak berarti itu bukan pilihan bagus. Jika Anda memahami eval dan bahayanya, itu bisa menjadi alat yang hebat untuk digunakan dalam situasi yang tepat.
dallin
4

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;))

kentaromiura
sumber
Dan seperti yang ditunjukkan oleh Crockford sendiri , browser web saat ini memiliki fungsi bawaan JSON.parse .
Ruud Helderman
4

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.

var a = eval("3 + 5");

Itu harus diatur sebagai

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

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,

  1. Pertama-tama, browser memanggil seluruh skrip di kotak pasir.
  2. Kode apa pun yang jahat dalam EVAL, adalah jahat di peramban itu sendiri. Penyerang atau siapa pun dapat dengan mudah menyuntikkan node script di DOM dan melakukan apa saja jika ia dapat mengevaluasi apa pun. Tidak menggunakan EVAL tidak akan membuat perbedaan.
  3. Sebagian besar keamanan sisi server buruk yang berbahaya. Validasi cookie yang buruk atau implementasi ACL yang buruk di server menyebabkan sebagian besar serangan.
  4. Kerentanan Java baru-baru ini, dll. Ada di dalam kode asli Java. JavaScript telah dan dirancang untuk berjalan di kotak pasir, sedangkan applet dirancang untuk berjalan di luar kotak pasir dengan sertifikat, dll. Yang mengarah pada kerentanan dan banyak hal lainnya.
  5. Menulis kode untuk meniru peramban tidaklah sulit. Yang harus Anda lakukan adalah membuat permintaan HTTP ke server dengan string agen pengguna favorit Anda. Lagi pula, semua alat pengujian membuat tiruan browser; jika seorang penyerang ingin melukaimu, EVAL adalah pilihan terakhir mereka. Mereka memiliki banyak cara lain untuk menangani keamanan sisi server Anda.
  6. Browser DOM tidak memiliki akses ke file dan bukan nama pengguna. Bahkan tidak ada pada mesin yang eval dapat memberikan akses.

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

"FirstName + ' ' + LastName"

Sebagai lawan

"LastName + ' ' + FirstName"

Seperti nama tampilan saya, yang bisa berasal dari basis data dan yang tidak hardcoded.

Akash Kava
sumber
Anda dapat menggunakan fungsi alih-alih eval - function (first, last) { return last + ' ' + first }.
Konrad Borowski
Nama kolom berasal dari basis data.
Akash Kava
3
Ancaman evalsebagian 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, mengubah valueopsi menjadi alert('PWNED!'), memilih opsi yang diubah, dan mengirimkan formulir. Sekarang, kapan saja orang lain dapat melihat nama tampilan saya, kode itu berjalan.
cao
@ cHao, Yang Anda bicarakan adalah contoh keamanan sisi server yang buruk, server tidak boleh menerima data yang dapat dieksekusi sebagai kode di browser siapa pun. Sekali lagi, Anda gagal memahami konsep keamanan sisi server yang buruk.
Akash Kava
1
Anda dapat mengeluh tentang keamanan sisi server jika Anda mau, tetapi intinya evaladalah 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), menghindari evalmembantu menghindarkan seluruh kategori masalah. Itu hal yang baik jika kode sisi server Anda kurang sempurna.
cHao
4

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:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

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 asli eval()dengan nama alias (katakan var noval = eval;dan kemudian dalam fungsi batin noval(expression);) maka evaluasi expressionmungkin gagal ketika mengacu pada variabel yang harus menjadi bagian dari penutupan, tetapi sebenarnya tidak.

Benjamin
sumber
3

Intinya

Jika Anda membuat atau membersihkan kode Anda eval, itu tidak pernah jahat .

Sedikit Lebih Detail

evalAdalah jahat jika berjalan di server menggunakan input yang dikirimkan oleh klien yang tidak dibuat oleh pengembang atau yang tidak dibersihkan oleh pengembang .

evaladalah 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 evaldirinya sendiri .

Steven Spungin
sumber
2

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.

Oli
sumber
Saat ini, ada cara lain (dan lebih baik) untuk memuat JavaScript secara asinkron dari server: w3bits.com/async-javascript
Ruud Helderman
1

evaljarang 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 dengan obj.prop) , penutupan, teknik berorientasi objek, teknik fungsional - gunakan saja.

yfeldblum
sumber
1

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.

javascript:alert("hello");

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.

<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>
Peter Mortensen
sumber
6
Re "Ada nol risiko dalam menggunakan pernyataan eval () ketika ada banyak cara yang lebih mudah untuk mengeksekusi javascript dan / atau memanipulasi objek di DOM". Injeksi kode adalah masalah ketika satu pengguna dapat memasukkan kode yang kemudian dijalankan di browser pengguna lain. Konsol browser tidak dengan sendirinya mengizinkan satu pengguna untuk menjalankan kode di browser pengguna lain sehingga mereka tidak relevan ketika memutuskan apakah perlu dilindungi terhadap injeksi kode.
Mike Samuel
Tidak <head></head>perlu, bahkan jika kosong?
Peter Mortensen
2
Jawaban ini sepenuhnya mengabaikan risiko XSS .
Ruud Helderman
1

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

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

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.

MichaelC
sumber
Bagus. Ketika saya mengajukan pertanyaan, saya bahkan tidak memikirkan JS di sisi server.
Richard Turner
1

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

Andre O
sumber
0

Tidak apa-apa untuk menggunakannya jika Anda memiliki kontrol penuh atas kode yang diberikan ke evalfungsi.

John Topley
sumber
2
Jika Anda memiliki kontrol penuh atas apa yang Anda sampaikan eval, maka pertanyaan besarnya menjadi, kapan masuk akal untuk itu menjadi string daripada JS nyata?
cao
@cHao Sebagai contoh, jika Anda memiliki Game-Aplikasi besar (5-10MB Javascript), lebih baik untuk membangun pertama-cepat AJAX-Preloader (1kb), yang memuat Main-Script besar, sambil menampilkan Loading- Bar atau yang serupa. Setelah mengunduh, Anda dapat menggunakan "eval (sumber)" atau lebih baik "Fungsi (sumber) baru" untuk menjalankan Script Game-Application-load. Dengan begitu pengguna dapat melihat secara visual bahwa Aplikasi perlu waktu untuk mengunduh hingga permainan dapat dimulai. Tanpa itu pengguna harus menunggu seluruh Aplikasi memuat tanpa umpan balik visual.
SammieFox
@SammieFox Ada cara lain (dan lebih baik) untuk melakukan ini, terutama <script async="true" src="...">. Lihat juga: w3bits.com/async-javascript
Ruud Helderman
Jawabannya adalah nasihat yang berbahaya; terlalu banyak pengembang yang memiliki perasaan salah untuk memegang kendali. Saran itu masuk akal untuk perangkat lunak yang tidak lagi dipelihara secara aktif. Tetapi perangkat lunak seperti itu harus dianggap mati.
Ruud Helderman
0

Hanya selama pengujian, jika memungkinkan. Perhatikan juga bahwa eval () jauh lebih lambat daripada evaluator JSON dll. Khusus lainnya.

Eric Wendelin
sumber
0

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.

Georg Schölly
sumber
Re "Tidak ada alasan untuk tidak menggunakan eval () selama Anda dapat memastikan bahwa sumber kode berasal dari Anda atau pengguna yang sebenarnya." Ini mengasumsikan bahwa ada satu pengguna. Premis itu tidak dinyatakan dalam OP. Ketika ada banyak pengguna, kecerobohan evalstring yang terdiri dari konten dari satu pengguna dapat memungkinkan pengguna untuk mengeksekusi kode di browser pengguna lain.
Mike Samuel
@ MikeSamuel, eval dapat mengeksekusi kode di browser pengguna lain, saya belum pernah mendengar ini, sudahkah Anda mencoba ini? Ini tidak pernah terjadi dalam riwayat penjelajahan, dapatkah Anda menunjukkan kepada kami sebuah contoh?
Akash Kava
@AkashKava, string dapat berasal dengan satu agen-pengguna, disimpan dalam database, dan kemudian disajikan ke browser lain yang evalmemilikinya. Itu terjadi setiap saat.
Mike Samuel
Basis data @MikeSamuel? dimana? siapa yang melayani string yang salah? Bukankah itu basis data di sisi server yang harus disalahkan? pertama-tama EVAL tidak dapat disalahkan untuk kode sisi server yang ditulis dengan buruk. Gunakan jsfiddle dan tunjukkan pada dunia contoh dunia nyata di mana ia dapat menyebabkan kerusakan.
Akash Kava
2
@AkashKava, saya tidak mengerti pertanyaan Anda. Kami tidak berbicara tentang aplikasi tertentu, tetapi alasan untuk tidak menggunakannya 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.
Mike Samuel
0

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.

PEZ
sumber
5
eval tidak diperlukan untuk setTimeout. Anda dapat menggunakan referensi fungsi di sana juga.
Matthew Crumley
0

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.ImageMapTypeobjek umum untuk saya, tetapi saya harus memberi tahu resepnya, bagaimana seharusnya membangun URL ubin dari parameter zoomdan coord:

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}
TMS
sumber
3
Sepertinya ini bisa dire-refored sehingga eval () tidak diperlukan - tileURLexpr hanyalah templat sehingga beberapa penggunaan ganti () yang bijaksana akan melakukan pekerjaan itu. Namun, itu mengingatkan saya pada contoh yang ada dalam pikiran saya ketika saya mengajukan pertanyaan, yang berkaitan dengan memungkinkan pengguna untuk menentukan rumus matematika untuk dievaluasi, mirip dengan fungsionalitas spreadsheet. Tentu saja saya tidak menyebutkan hal itu pada saat itu karena saya tidak ingin mempengaruhi jawaban!
Richard Turner
8
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; },
Casey Chu
0

Contoh saya menggunakan eval: import .

Bagaimana biasanya dilakukan.

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

Tetapi dengan bantuan evaldan fungsi pembantu kecil itu mendapatkan tampilan yang jauh lebih baik:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable mungkin terlihat seperti (versi ini tidak mendukung impor anggota konkret).

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}
Yaroslav
sumber
2
1 untuk ide, tetapi Anda memiliki bug di sini: .replace(/name/g, name).replace('path', path). Jika nameberisi string "path"maka Anda bisa mendapatkan kejutan.
wberry
1
Mendeklarasikan satu variabel untuk setiap properti componentsadalah 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 harus evaldijauhkan dari kode produksi.
Ruud Helderman
0

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.

const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something
Jemiloii
sumber
-1

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 menemukan eval()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 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 .

Diwarnai
sumber
"Hyperscript dihasilkan sebagai string terlebih dahulu (...)" Lebih masuk akal untuk melakukan semua pembuatan kode dalam fase build, menulis kode hyperscript yang dihasilkan ke file executable (.js) yang terpisah, kemudian gunakan file itu untuk menguji dan produksi. Saya suka cara Anda menggunakan pembuatan kode. Hanya saja itu evaladalah petunjuk bahwa beberapa tanggung jawab yang termasuk dalam waktu kompilasi, telah pindah ke runtime.
Ruud Helderman
-1

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.

Peter Mortensen
sumber
1
Penggunaan evalkebutuhan harus diamankan terhadap serangan XSS, yang tidak selalu mudah untuk diperbaiki.
Benjamin
-1

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.

Erik Haliewicz
sumber
Entah Anda menulis kompiler (yang menyimpan daripada mengeksekusi kode yang dihasilkan) atau Anda menulis interpreter (di mana setiap instruksi memiliki implementasi yang telah dikompilasi sebelumnya). Tidak ada kasus penggunaan untuk eval.
Ruud Helderman
Jika Anda membuat kode javascript dan ingin segera menjalankannya (katakanlah untuk manfaat kinerja daripada interpretasi langsung), itu akan menjadi kasus penggunaan untuk eval.
Erik Haliewicz
Poin bagus; Saya melihat contoh di artikel ini tentang Blockly . Saya kaget Google merekomendasikan 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).
Ruud Helderman
-5

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.

vitmalina
sumber
4
Jangan gunakan secara membabi buta 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".
Rob W
2
Jika tidak menggunakan tanda kutip ganda di sekitar nama properti, itu mungkin representasi string dari objek literal, tapi itu bukan JSON . JSON mendefinisikan nama properti sebagai a stringdan mendefinisikan stringsebagai urutan nol atau lebih karakter Unicode, dibungkus dengan tanda kutip ganda, menggunakan backslash escapes.
Kode Tidak Berguna
Lihat artikel oleh Nikolas Zakas - "eval () tidak jahat, hanya disalahpahami" nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood
vitmalina
@vitmalina Dari artikel Zakas: "Ini bisa berbahaya jika Anda mengambil input pengguna dan menjalankannya melalui eval (). Namun, jika input Anda bukan dari pengguna, apakah ada bahaya nyata?" Itulah masalahnya. Setelah kode Anda tumbuh melebihi proporsi 'hello world', dengan cepat menjadi tidak mungkin untuk membuktikan bahwa Anda tidak membocorkan masukan pengguna eval. Dalam aplikasi web multi-penyewa serius apa pun, dengan puluhan pengembang bekerja pada basis kode yang sama, ini tidak dapat diterima.
Ruud Helderman