Почему использование функции JavaScript eval - плохая идея?

StackOverflow https://stackoverflow.com/questions/86513

  •  01-07-2019
  •  | 
  •  

Вопрос

Функция eval - это мощный и простой способ динамической генерации кода, так в чем же заключаются предостережения?

Это было полезно?

Решение

  1. Неправильное использование оценка открывает ваш код для инъекционных атак

  2. Отладка может быть сложнее (без номеров строк и т.д.)

  3. оцениваемый код выполняется медленнее (нет возможности скомпилировать / кэшировать оцениваемый код).

Редактировать:Как отмечает @Jeff Walden в комментариях, # 3 сегодня менее актуально, чем это было в 2008 году.Однако, хотя может произойти некоторое кэширование скомпилированных скриптов, это будет ограничено только скриптами, которые повторяются без изменений.Более вероятным сценарием является то, что вы оцениваете скрипты, которые каждый раз претерпевали незначительные изменения и поэтому не могли быть кэшированы.Давайте просто скажем, что КАКОЙ-ТО оцениваемый код выполняется медленнее.

Другие советы

оценка - это не всегда зло.Бывают моменты, когда это совершенно уместно.

Однако eval в настоящее время и исторически массово используется людьми, которые не знают, что они делают.К сожалению, это включает в себя людей, пишущих учебные пособия по JavaScript, и в некоторых случаях это действительно может иметь последствия для безопасности - или, чаще, простые ошибки.Так что чем больше мы сможем сделать, чтобы поставить вопросительный знак над eval, тем лучше.Каждый раз, когда вы используете eval, вам нужно проверять разумность того, что вы делаете, потому что, скорее всего, вы могли бы делать это лучше, безопаснее и чище.

Чтобы привести слишком типичный пример, нужно задать цвет элемента с идентификатором, хранящимся в переменной 'potato':

eval('document.' + potato + '.style.color = "red"');

Если бы авторы приведенного выше кода имели представление об основах работы объектов JavaScript, они бы поняли, что вместо буквенных имен с точками можно использовать квадратные скобки, что устраняет необходимость в eval:

document[potato].style.color = 'red';

...который намного легче читать, а также менее потенциально глючит.

(Но тогда кто-то, кто / действительно / знал, что делает, сказал бы:

document.getElementById(potato).style.color = 'red';

это более надежно, чем старый хитроумный трюк с доступом к элементам DOM прямо из объекта document.)

Я полагаю, это потому, что он может выполнить любую функцию JavaScript из строки.Его использование упрощает для людей внедрение вредоносного кода в приложение.

На ум приходят два момента:

  1. Безопасность (но пока вы сами генерируете строку для оценки, это может быть не проблемой)

  2. Производительность:пока код, который будет выполняться, неизвестен, он не может быть оптимизирован.(о javascript и производительности, конечно Презентация Стива Йегге)

Передача пользовательских данных в eval() представляет угрозу безопасности, но также каждый вызов eval() создает новый экземпляр интерпретатора JavaScript.Это может быть проблема с ресурсами.

Как правило, это проблема только в том случае, если вы передаете пользовательский ввод eval.

В основном, его намного сложнее поддерживать и отлаживать.Это похоже на goto.Вы можете использовать его, но это затрудняет поиск проблем и затрудняет работу людей, которым, возможно, потребуется внести изменения позже.

Следует иметь в виду одну вещь: вы часто можете использовать eval() для выполнения кода в среде с ограниченным доступом - сайты социальных сетей, которые блокируют определенные функции JavaScript, иногда можно обмануть, разбив их на блоки eval -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

Итак, если вы хотите запустить какой-то код JavaScript, где в противном случае это могло бы быть запрещено (Myspace - Myspace, я смотрю на тебя ...) тогда eval() может быть полезным трюком.

Однако, по всем упомянутым выше причинам, вы не должны использовать его для своего собственного кода, где у вас есть полный контроль - в нем просто нет необходимости, и его лучше отнести на полку "хитрых JavaScript-хаков".

Если вы не разрешите eval() создавать динамический контент (через cgi или input), он будет таким же безопасным и надежным, как и все остальные JavaScript на вашей странице.

Наряду с остальными ответами, я не думаю, что операторы eval могут иметь расширенную минимизацию.

Это возможный риск для безопасности, он имеет другую область выполнения и довольно неэффективен, поскольку создает совершенно новую скриптовую среду для выполнения кода.Смотрите здесь для получения дополнительной информации: оценка.

Тем не менее, это довольно полезно, и при умеренном использовании может добавить много хорошей функциональности.

Если вы не уверены на 100%, что оцениваемый код взят из надежного источника (обычно из вашего собственного приложения), то это верный способ подвергнуть вашу систему межсайтовой скриптовой атаке.

Я знаю, что эта дискуссия старая, но мне действительно нравится это я обратился к Google и хотел поделиться этим чувством с другими ;)

Другое дело, что чем лучше у Вас получается, тем больше Вы пытаетесь понять и, в конце концов, Вы просто не верите, что что-то хорошо или плохо только потому, что кто-то так сказал :) Это очень вдохновляющий Видео это помогло мне больше думать самостоятельно :) ХОРОШИЕ ПРАКТИКИ хороши, но не используйте их бездумно :)

Это не обязательно так уж плохо, если вы знаете, в каком контексте вы это используете.

Если ваше приложение использует eval() чтобы создать объект из некоторого JSON, который вернулся из XMLHttpRequest - запрос для вашего собственного сайта, созданного вашим надежным серверным кодом, это, вероятно, не проблема.

Ненадежный клиентский JavaScript-код в любом случае не может сделать так много.При условии, что то, что вы выполняете eval() если информация поступила из разумного источника, с вами все в порядке.

Это значительно снижает ваш уровень уверенности в безопасности.

Если вы хотите, чтобы пользователь вводил некоторые логические функции и вычислял для И ИЛИ, то функция JavaScript eval идеально подходит.Я могу принять две строки и eval(uate) string1 === string2, и т.д.

Если вы заметили использование eval() в своем коде, помните мантру “eval() - это зло”.

Эта функция принимает произвольную строку и выполняет ее как код JavaScript.Когда код, о котором идет речь в вопросе, известен заранее (не определен во время выполнения), нет причин использовать eval().Если код генерируется динамически во время выполнения, часто есть лучший способ достичь цели без eval().Например, просто использовать обозначения в квадратных скобках для доступа к динамическим свойствам лучше и проще:

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

Используя eval() также имеет последствия для безопасности, потому что вы можете быть выполнение кода (по пример приходящих из сети), который был подделан.Это обычный антипаттерн при работе с JSON-ответом на Ajax-запрос.В тех случаях, лучше использовать браузеров встроенных методов для анализа ответа JSON, чтобы сделать уверен, что это безопасно и допустимо.Для браузеров, которые не поддерживают JSON.parse() изначально вы можете использовать библиотеку из JSON.org.

Также важно помнить, что передача строк в setInterval(), setTimeout(), и Function() конструктор, по большей части, аналогичен использованию eval() и поэтому следует избегать.

За кулисами JavaScript все еще должен оценивать и выполнять строку, которую вы передаете как программный код:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

Использование конструктора new Function() аналогично eval(), и к нему следует подходить с осторожностью.Это может быть мощная конструкция, но ею часто злоупотребляют.Если вам абсолютно необходимо используйте eval(), вы можете рассмотреть возможность использования вместо этого новой функции().

Существует небольшой потенциал преимущество, поскольку код, оцениваемый в new Function(), будет выполняться в локальной функции область видимости, поэтому любые переменные, определенные с помощью var в оцениваемом коде, не станут глобальными автоматически.

Другой способ предотвратить автоматические глобальные переменные - это обернуть eval() вызовите немедленную функцию.

Помимо возможных проблем с безопасностью, если вы выполняете код, отправленный пользователем, в большинстве случаев есть лучший способ, который не требует повторного разбора кода при каждом его выполнении.Анонимные функции или свойства объекта могут заменить большинство способов использования eval и намного безопаснее и быстрее.

Это может стать еще большей проблемой, поскольку следующее поколение браузеров выйдет с некоторыми элементами компилятора JavaScript.Код, выполняемый с помощью Eval, может работать не так хорошо, как остальной ваш JavaScript, в этих новых браузерах.Кто-то должен провести некоторое профилирование.

Это одна из хороших статей, рассказывающих об eval и о том, что это не зло:http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

Я не говорю, что вы должны выбежать и начать использовать eval() везде.На самом деле, существует очень мало хороших вариантов использования для запуска eval() вообще.Определенно существуют проблемы с ясностью кода, возможностью отладки и, конечно же, производительностью, которые не следует упускать из виду.Но вы не должны бояться использовать его, когда у вас есть случай, когда eval() имеет смысл.Попробуйте сначала не использовать его, но не позволяйте никому напугать вас, заставив думать, что ваш код более хрупкий или менее безопасный, когда eval() используется надлежащим образом.

eval() является очень мощным и может использоваться для выполнения инструкции JS или вычисления выражения.Но вопрос не в использовании eval(), а давайте просто скажем, как вредоносная сторона влияет на строку, которую вы запускаете с помощью eval().В конце вы запустите вредоносный код.Вместе с властью приходит большая ответственность.Так что используйте это с умом - вы это используете.Это не имеет большого отношения к функции eval (), но в этой статье содержится довольно хорошая информация: http://blogs.popart.com/2009/07/javascript-injection-attacks/ Если вы ищете основы eval (), посмотрите здесь:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

Я не буду пытаться опровергнуть что-либо из сказанного ранее, но я предложу использовать eval(), что (насколько я знаю) невозможно сделать никаким другим способом.Вероятно, есть другие способы закодировать это и, вероятно, способы его оптимизации, но это делается вручную и без всяких наворотов для наглядности, чтобы проиллюстрировать использование eval, у которого на самом деле нет никаких других альтернатив.Это:динамические (или, более точно) программно созданные имена объектов (в отличие от значений).

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

Редактировать:кстати, я бы не стал предлагать (по всем указанным ранее соображениям безопасности), чтобы вы основывали имена своих объектов на вводимых пользователем данных.Хотя я не могу представить себе ни одной веской причины, по которой вы захотели бы это сделать.Тем не менее, я подумал, что стоит указать на то, что это было бы не очень хорошей идеей :)

Движок JavaScript имеет ряд оптимизаций производительности, которые он выполняет на этапе компиляции.Некоторые из них сводятся к возможности по существу статически анализировать код по мере его загрузки и предварительно определять, где находятся все объявления переменных и функций, чтобы меньше усилий требовалось для разрешения идентификаторов во время выполнения.

Но если Движок находит eval(..) в коде, он, по сути, должен предположить, что вся его осведомленность о местоположении идентификатора может быть недействительной, потому что он не может точно знать во время лексирования, какой код вы можете передать eval (..) для изменения лексической области, или содержимое объекта, к которому вы можете перейти, чтобы создать новую лексическую область для ознакомления.

Другими словами, в пессимистическом смысле большинство из тех оптимизаций, которые он мог бы выполнить, бессмысленны, если присутствует eval(..), поэтому он просто не выполняет оптимизацию вообще.

Это все объясняет.

Ссылка :

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance

Это не всегда плохая идея.Возьмем, к примеру, генерацию кода.Недавно я написал библиотеку под названием Гипербары который устраняет разрыв между виртуальный дом и руль.Он делает это путем синтаксического анализа шаблона handlebars и преобразования его в гиперскриптум который впоследствии используется virtual-dom.Гиперскрипт сначала генерируется в виде строки, а затем возвращается, eval() это для того, чтобы превратить его в исполняемый код.Я нашел eval() в данной конкретной ситуации это полная противоположность злу.

В основном из

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

К этому

(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({}))

Производительность eval() это не проблема в подобной ситуации, потому что вам нужно только один раз интерпретировать сгенерированную строку, а затем повторно использовать исполняемый вывод много раз.

Вы можете посмотреть, как была достигнута генерация кода, если вам интересно здесь.

Я бы зашел так далеко, что сказал, что на самом деле не имеет значения, используете ли вы eval() в javascript, который запускается в браузерах.* (предостережение)

Во всех современных браузерах есть консоль разработчика, где вы можете выполнять произвольный javascript в любом случае, и любой мало-мальски умный разработчик может посмотреть на ваш исходный код JS и поместить любые его фрагменты в консоль разработчика, чтобы делать то, что он хочет.

* До тех пор, пока конечные точки вашего сервера имеют правильную проверку и очистку предоставленных пользователем значений, не должно иметь значения, что анализируется и оценивается в вашем клиентском javascript.

Если бы вы спросили, подходит ли он для использования eval() однако в PHP ответ таков НЕТ, если только вы не белый список любые значения, которые могут быть переданы в ваш оператор eval.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top