2013-05-27 3 views
2

Существует предыдущий вопрос (here), однако ответы там точно не отвечают на мой вопрос - принятый ответ содержит недействительный JSON (обязательный для использования eval()) что, насколько мне известно, просто невозможно сделать даже что-то подобное.Функции анализа, хранящиеся в виде строк в объектном литерале/синтаксис JSON и различающийся

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

В настоящее время, я думал о следующих возможностях:

  1. Просто используйте eval() для разбора строки
  2. Плейс функции в виде строки (с чем-то вроде "\bfunction", чтобы быть в состоянии идентифицировать их), бег JSON.parse() на него, а затем использовать для в цикле, чтобы увидеть нужно ли такие функции, которые будут анализироваться (вероятно, довольно медленно)
  3. используйте DOM для запуска кода с использованием <script> тег и просто запустить его там

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

EDIT: будет ли альтернативный синтаксис для синтаксиса лучше или это просто сделает вещи еще более сложными?

EDIT2: Я ищу, просто сделать что-то вроде следующего:

{ "test": function() {} 
, "foo": 1 
, "bar": 2 } 

Я не ищет просто разобрать целую функцию из строки, например

eval('function(){}'); 
+1

использовать параметр reviver для JSON.parse и попробовать {} найти функции внутри. с другой стороны, если все данные поступают от вас, eval() абсолютно безопасен в использовании, и почти все, что вы можете сделать с помощью eval(), вы можете сделать с помощью функции(). – dandavis

+0

Для чего вам нужны функции? Не можете ли вы просто создать общую функцию (фабрику) и отправить только JSONifiable параметры для этого? – Bergi

+0

@Bergi: Я бы хотел гибко определить пользовательские функции, некоторые из которых нужно добавить просто для возможностей, добавленных через JSON. –

ответ

2

Эффективность (? Будут ли они делать то, что они должны делать)

Все 1, 2, и 3 будет работать

  1. Eval в responseText: Это будет работать нормально.Если вы добавите одноименную проблему в отношении рекомендации № 3 по безопасности, вы должны ее исправить сначала.

  2. Определите отдельные предметы в объекте, чтобы «оживить». Я использовал этот подход раньше и предпочитаю использовать соглашение об именах для ключей, чтобы определить, когда нужно возрождать, а не маркер в значении. См. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse для описания регенераторов.

    В обоих случаях 1 и 2 вам придется каким-то образом обработать текст функции &. Вот ваши варианты. Нет на самом деле менее зла, чем другие (см. Раздел о безопасности).

    Если вы используете f = eval (responseText), это «прямая оценка». Функция будет иметь локальную область, в которой вызывается eval.

    Если вы используете f = (1, eval) (responseText), это «косвенная оценка». Функция будет иметь глобальный охват. См. http://perfectionkills.com/global-eval-what-are-the-options/

    Если вы используете f = new Function (responseText), функция будет иметь отдельную локальную область под глобальной областью.

  3. Этот метод называется JSONP (для JSON с дополнением). Он будет работать и, вероятно, проще всего реализовать. Также не хорошо, когда ответ может содержать конфиденциальные пользовательские данные (см. Ниже).

безопасности (Будут ли они не делают то, что они не должны делать?)

ЕСЛИ доставленное сообщение под вашим контролем (100% уверен, что ни один пользователь/злоумышленник не может изменить его): Все варианты 1, 2 и 3 НЕ нарушают состояние браузера пользователя (например, XSS, например, позволяют использовать такие вещи, как кража их файлов cookie).

ЕСЛИ доставленное сообщение НЕ является 100%, безусловно, под вашим контролем: Все методы 1, 2 и 3 делают компрометацию состояния браузера пользователя и являются небезопасными. Ваши варианты:

  • Принять риск безопасности (пользователи/злоумышленники смогут произвольно изменить функциональные возможности сайта, включая, но не ограничиваясь: кража куки ваших пользователей для домена, кражи любой информации, пользователь имеет доступ к вашему домену, направляя ваших пользователей на зараженные вредоносными страницами потенциально инсталлированные вирусы)

  • Передайте потенциально опасные функции веб-работнику и запустите их там. Они будут изолированы и не могут повлиять на окно браузера. Однако это больше работает и недоступно во всех браузерах.

  • Передайте потенциально опасные функции в iframe в отдельном домене и запустите их там. Это защищает файлы cookie ваших пользователей и т. Д.но не мешает злоумышленникам перенаправлять их на использование сайтов для установки вирусов (хотя вы можете просто надеяться, что ваши пользователи имеют безопасные браузеры: - /)

  • Используйте белые функции, которые вы контролируете, и просто передавайте имя белого списка функция (или фабричные функции, которые, по существу, белый список функций с несколькими гибкими параметрами)

Если вы передаете пользователем конкретную информацию в ваших данных:

  • 3 небезопасен для передачи конфиденциальных пользовательских данных (сценарий может быть легко вызван с помощью файлов cookie пользователя из любого домена), если только ваш сервер не выполняет проверки заголовков HTTP-отправителя/источника запроса перед возвратом ответа. Даже тогда это может быть небезопасно. См http://en.wikipedia.org/wiki/Cross-site_request_forgery

  • The наивных реализации вариантов 1, 2, также небезопасны таким образом (т.е. XSRF., Например, злонамеренный сайт может сформировать запрос на данные от имени пользователя, используя свой куки, а затем делать то, что им нравится с ним). Домен третьей стороны может перегрузить встроенный конструктор Array, а затем вставить тег скрипта, указывающий на файл JSON. Браузер запросит этот файл с помощью файлов cookie пользователя, вернет JSON пользователя на сторонний сайт, а механизм javascript «запустит» JSON, который является всего лишь одним оператором, но поскольку конструктор Array перегружен, он может делать все, что захочет, с данными, поскольку javascript пытается построить значение в этом одиночном JSON-заявлении

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

+0

Вот относительно простой способ безопасно выявить довольно широкий класс функций: http://www.adsafe.org/ –

0

Вы действительно не имеете много вариантов, но есть четвертая альтернатива (JQuery использует этот подход):

var yourFunction = new Function("var a=1; return a; //this is your function body"); 
1

Есть много способов, чтобы создать функцию JS из строки, но eval - evil и его следует избегать, когда это возможно.

Вы можете создать функцию по-разному, например, используя оператор new Function("...") или создать функцию в своей области с этим именем, а затем вызвать ее с параметрами в виде строки. Я проверил JSFiddle с тестом. Самый быстрый способ - eval, но, как я уже говорил, его следует избегать. DOM и new Function() пути одинаково быстры, на 1000 итераций они имеют разницу в несколько миллисекунд (fn: 3328, DOM: 3371) Здесь у вас есть JSFiddle, сделайте несколько тестов и сделайте собственные выводы.

Основное отличие между eval и new Function состоит в том, что первые могут получить доступ к локальным переменным, в то время как последний не может. См. Это answer.

2

Вы в основном есть только два варианта, чтобы принести код функции клиента:

  • Используйте JavaScript литерал объекта, в котором вы включаете функцию выражения. Используете ли вы AJAX + eval или подход JSONP-like с узлом <script>, это не имеет большого значения с точки зрения производительности. AJAX будет более гибким (методы HTTP, синхронные запросы) и не нуждается в глобальной функции обратного вызова.

    Использование выражения JS даст вам всю возможную свободу, включая пользовательские типы данных (например, объекты Date или вызовы собственных конструкторов) или даже круговые структуры (если они завернуты в IEFE). Однако сериализация таких данных на сервере в скрипте будет сложнее, возможно, потребуется обработать вручную и более подвержена ошибкам (синтаксис или даже ошибки времени выполнения приведут к сбою всего анализа).

  • Используйте valid JSON, а затем создайте функции из строк кода, о которых вы знаете. Использование этого стандартизованного формата упростит сериализацию серверов и сделает ваши данные доступными для клиентов без JS-интерпретатора. Этот подход очень распространен, большинство людей, которые используют объекты с пользовательскими прототипами, хорошо привыкли к этой задаче. Вместо этого вы будете использовать только Function constructor.

    В зависимости от вашей схемы вы можете либо перебрать/получить доступ к анализируемой структуре, либо переопределить соответствующие свойства, либо использовать аргумент обратного вызова reviver для JSON.parse. Пример:

    var json = '{"validators":[{"name":"required", "message":"must be filled", "fn":["str", "return str.trim().length > 0;"]}, {…}, …]}'; 
    var result = JSON.parse(json, function(k, v) { 
        if (k != "fn") return v; 
        return Function.apply(null, v); 
    }); 
    
0

Есть несколько вещей, которые необходимо учитывать для вашей конкретной ситуации.

Я думаю, что эмпирическое правило должно быть, если вы не уверены, следует ли использовать eval, вы, вероятно, не должны его использовать. Я также думаю, что некоторые люди зацикливаются на производительности, когда операции в секунду могут быть незначительными. Является ли ваше приложение огромным приложением, которое полагается на сжатие каждой унции производительности? Будет ли 100,000ops/sec против 1,000,000ops/sec, будет ли разница в том, можно ли использовать ваше приложение? Также вам нужно будет рассмотреть поддержку, поскольку все браузеры не поддерживают поддержку JSON.

Тем не менее, моя первая мысль:

Предполагая, что вы должны иметь эти указанные функции посылают через JSON (который звучит немного странно для меня), и у вас есть контроль над тем, что сервер посылает вам, будет обозначьте свои функции префиксом, как js-. Если они должны быть выполнены, как только они будут получены, вы могли бы лишить js- от использования substr и выполнить его с помощью кронштейна обозначения что-то вроде:

//loop over values 
    if (value[i].substr && value[i].indexOf('js-') === 0) { 
     var func = value[i].substr(3); 

     //assuming this is a global function you're calling 
     window[func](); 
    } 

Если вы не пытаетесь выполнить их немедленно, возможно, добавить их в литерал массива/объекта для вызова позже.