Ограничение eval () в узкой области - PullRequest
29 голосов
/ 13 февраля 2009

У меня есть файл javascript, который читает другой файл, который может содержать фрагменты javascript, которые должны быть eval () - ed. Предполагается, что фрагменты скрипта соответствуют строгому подмножеству javascript, ограничивающему то, что они могут делать, и какие переменные они могут изменять, но я хочу знать, есть ли какой-то способ обеспечить это, не давая eval видеть переменные в глобальной области видимости , Примерно так:

function safeEval( fragment )
{
    var localVariable = g_Variable;

    {
        // do magic scoping here so that the eval fragment can see localVariable
        // but not g_Variable or anything else outside function scope

        eval( fragment );
    }
}

Фактический код не должен выглядеть следующим образом - я открыт для любых странных уловок с замыканиями и т. Д. Но я хочу знать, возможно ли это даже .

Ответы [ 8 ]

52 голосов
/ 13 февраля 2009

Краткий ответ : Нет. Если он находится в глобальной области видимости, он доступен любому.

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

function maskedEval(scr)
{
    // set up an object to serve as the context for the code
    // being evaluated. 
    var mask = {};
    // mask global properties 
    for (p in this)
        mask[p] = undefined;

    // execute script in private context
    (new Function( "with(this) { " + scr + "}")).call(mask);
}

Опять же, я должен подчеркнуть:

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

3 голосов
/ 09 апреля 2017

Shog9 ♦ Ответ великолепен. Но если ваш код является просто выражением, код будет выполнен и ничего не будет возвращено. Для выражений используйте

function evalInContext(context, js) {

  return eval('with(context) { ' + js + ' }');

}

Вот как это использовать:

var obj = {key: true};

evalInContext(obj, 'key ? "YES" : "NO"');

Вернется "YES".

Если вы не уверены, является ли выполняемый код выражениями или операторами, вы можете объединить их:

function evalInContext(context, js) {

  var value;

  try {
    // for expressions
    value = eval('with(context) { ' + js + ' }');
  } catch (e) {
    if (e instanceof SyntaxError) {
      try {
        // for statements
        value = (new Function('with(this) { ' + js + ' }')).call(context);
      } catch (e) {}
    }
  }

  return value;
}
3 голосов
/ 12 августа 2013

Вот идея. Что делать, если вы использовали статический анализатор (что-то, что вы могли бы построить, например, с помощью esprima ), чтобы определить, какие внешние переменные использует код eval'd, и присвоить им псевдоним. Под «внешним кодом» я имею в виду переменные, в которых eval'd-код использует , но не объявляет . Вот пример:

eval(safeEval(
     "var x = window.theX;"
    +"y = Math.random();"
    +"eval('window.z = 500;');"))

где safeEval возвращает строку javascript, измененную с контекстом, который блокирует доступ к внешним переменным:

";(function(y, Math, window) {"
  +"var x = window.theX;"
  +"y = Math.random();"
  +"eval(safeEval('window.z = 500;');"
"})();"

Есть несколько вещей, которые вы можете теперь сделать с этим:

  • Вы можете убедиться, что eval'd-код не может ни читать значения внешних переменных, ни записывать их (передавая undefined в качестве аргументов функции или не передавая аргументы). Или вы можете просто вызвать исключение в случаях небезопасного доступа к переменным.
  • Вы также гарантируете, что переменные, созданные eval, не влияют на окружающую область видимости
  • Вы можете разрешить eval создавать переменные в окружающей области видимости, объявив эти переменные вне замыкания, а не в качестве параметров функции
  • Вы можете разрешить доступ только для чтения, копируя значения внешних переменных и используя их в качестве аргументов функции
  • Вы можете разрешить доступ на чтение и запись к определенным переменным, сказав safeEval, чтобы они не называли эти конкретные имена
  • Вы можете обнаружить случаи, когда eval не изменяет конкретную переменную и позволяет автоматически исключать ее из псевдонима (например, Math в этом случае не изменяется)
  • Вы могли бы дать eval контекст для запуска, передав значения аргументов, которые могут отличаться от окружающего контекста
  • Вы можете фиксировать изменения контекста, также возвращая аргументы функции из функции, чтобы вы могли просматривать их вне eval.

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

Конечно, выполнение всей этой работы может замедлить ваш код, но есть места, где попадание не имеет значения. Надеюсь, это кому-нибудь поможет. И если кто-нибудь создаст доказательство концепции, я хотел бы увидеть ссылку на него здесь; )

2 голосов
/ 25 мая 2014

Подобно сценарию динамического переноса функции в блочном подходе with, описанном выше, это позволяет добавлять псевдоглобальные переменные в код, который вы хотите выполнить. Вы можете «скрыть» определенные вещи, добавив их в контекст.

function evalInContext(source, context) {
    source = '(function(' + Object.keys(context).join(', ') + ') {' + source + '})';

    var compiled = eval(source);

    return compiled.apply(context, values());

    // you likely don't need this - use underscore, jQuery, etc
    function values() {
        var result = [];
        for (var property in context)
            if (context.hasOwnProperty(property))
                result.push(context[property]);
        return result;
    }
}

См. http://jsfiddle.net/PRh8t/ для примера. Обратите внимание, что Object.keys поддерживается не во всех браузерах.

2 голосов
/ 13 февраля 2009

Вы не можете ограничить область действия eval

кстати см. этот пост

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

1 голос
/ 18 ноября 2013

Не используйте eval. Существует альтернатива, js.js: интерпретатор JS, написанный на JS , так что вы можете запускать программы JS в любой среде, которую вам удалось настроить. Вот пример его API со страницы проекта:

var jsObjs = JSJS.Init();
var rval = JSJS.EvaluateScript(jsObjs.cx, jsObjs.glob, "1 + 1");
var d = JSJS.ValueToNumber(jsObjs.cx, rval);
window.alert(d); // 2
JSJS.End(jsObjs);

Ничего страшного, как видите.

1 голос
/ 05 июня 2013

Существует проект под названием Google Caja. Вы можете "песочницей" сторонний javascript, используя Caja. https://developers.google.com/caja/

0 голосов
/ 07 февраля 2015

Не выполняйте код, которому вы не доверяете. Глобалы всегда будут доступны. Если вы доверяете коду, вы можете выполнить его с определенными переменными в области видимости следующим образом:

(new Function("a", "b", "alert(a + b);"))(1, 2);

это эквивалентно:

(function (a, b) {
    alert(a + b);
})(1, 2);
...