Есть ли способ заключить в тюрьму в Javascript, так что DOM не виден

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

Пример: если интерфейс checkanswer(func) это разрешено:

checkanswer( function (x,y)={
   return x+y;
}

но это не разрешено:
alert(1)
document.write("hello world")
eval("alert()")

РЕДАКТИРОВАТЬ: я имел в виду простой язык, который был реализован с использованием JavaScript, что-то вроде http://stevehanov.ca/blog/index.php?id=92

7 ответов

Решение

(Редактировать Этот ответ относится к вашему вопросу перед редактированием. Не знаю ни одного языка сценариев, реализованного с использованием Javascript, хотя я ожидаю, что они есть. Например, в какой-то момент кто-то написал BASIC для Javascript (раньше имел ссылку, но это гнило). Остальная часть этого ответа, таким образом, довольно академична, но я оставил его только для обсуждения, иллюстрации и даже для предостережения. Кроме того, я определенно согласен с замечаниями Бобинса - не делайте этого сами, используйте работу других, таких как Каха.)

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

Из-за способа, которым Javascript выполняет разрешение символов, кажется, что должна быть возможность оценить сценарий в контексте, где window, document, ActiveXObject, XMLHttpRequestи подобные не имеют своего обычного значения:

// Define the scoper
var Scoper = (function() {
    var rv = {};

    rv.scope = function(codeString) {
        var window,
            document,
            ActiveXObject,
            XMLHttpRequest,
            alert,
            setTimeout,
            setInterval,
            clearTimeout,
            clearInterval,
            Function,
            arguments;
            // etc., etc., etc.

        // Just declaring `arguments` doesn't work (which makes
        // sense, actually), but overwriting it does
        arguments = undefined;

        // Execute the code; still probably pretty unsafe!
        eval(codeString);
    };

    return rv;;
})();

// Usage:
Scoper.scope(codeString);

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

Но это не работает, это только частичное решение (подробнее ниже). Логика заключается в том, что любая попытка внутри кода в codeString чтобы получить доступ window (например) будет обращаться к локальной переменной windowне глобальный; и то же самое для других. К сожалению, из-за способа разрешения символов любое свойство window можно получить доступ с или без window. префикс (alertнапример), поэтому вы должны перечислить их тоже. Это может быть длинный список, не в последнюю очередь потому, что, как указывает bobince, IE выдает любой элемент DOM с именем или идентификатором на window, Таким образом, вам, вероятно, придется поместить все это в свой собственный iframe, чтобы вы могли в конце концов обойти эту проблему и "только" иметь дело со стандартными вещами. Также обратите внимание, как я сделал scope Функция свойство объекта, а затем вы вызываете его только через свойство. Вот так вот this установлен на Scoper экземпляр (в противном случае, при необработанном вызове функции, this по умолчанию window!).

Но, как указывает Бобинс, есть очень много разных способов достичь цели. Например, этот код в codeString успешно ломает тюрьму выше:

(new ('hello'.constructor.constructor)('alert("hello from global");'))()

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

(function(){return this;})().alert("hello again from global!");

Отсюда и "гонка вооружений".

Единственный действительно тщательный способ сделать это - встроить в ваш сайт правильный анализатор Javascript, проанализировать его код и проверить его на несанкционированный доступ, и только после этого запускать код. Это много работы, но если ваш сценарий использования оправдывает это...

Ти Джей Краудер замечательно говорит о "гонке вооружений". Будет очень сложно построить водонепроницаемую песочницу.

однако некоторые функции можно переопределить довольно легко.

Простые функции:

И в соответствии с этим вопросом, даже такие важные вещи, как document.write так же просто, как

document.write = function(str) {}

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

Альтернативные варианты:

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

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

  • Есть несколько методов для отслеживания DOM на предмет изменений, и я уверен, что можно создать механизм, который немедленно отменит любые изменения, очень похожий на управление DLL в Windows. Но это будет очень сложно построить и очень ресурсоемко.

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

Не пытайтесь взять это на себя. Подумайте об использовании существующего проекта "mini-JS-like-language", такого как Caja.

Я получил другой способ: использовать Google Gears WorkerPool API
Видеть это
http://code.google.com/apis/gears/api_workerpool.html

Созданный работник не имеет доступа к DOM; такие объекты, как документ и окно, существуют только на главной странице. Это является следствием того, что работники не разделяют никакого состояния исполнения. Однако работники имеют доступ ко всем встроенным функциям JavaScript. Большинство методов Gears также можно использовать через глобальную переменную, которая определяется автоматически: google.gears.factory. (Единственным исключением является отправитель файла LocalServer, для которого требуется DOM.) Для других функций созданные работники могут запрашивать выполнение главной страницы на главной странице.

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

Как насчет этого шаблона для реализации песочницы?

function safe(code,args)
{
    if (!args)
        args=[];
    return (function(){
    for (i in window) 
        eval("var "+i+";");
    return function(){return eval(code);}.apply(0,args);
    })();
}



ff=function()
{
    return 3.14;
}

console.log(safe("this;"));//Number
console.log(safe("window;"));//undefined
console.log(safe("console;"));//undefined
console.log(safe("Math;"));//MathConstructor
console.log(safe("JSON;"));//JSON
console.log(safe("Element;"));//undefined
console.log(safe("document;"));//undefined
console.log(safe("Math.cos(arguments[0]);",[3.14]));//-0.9999987317275395
console.log(safe("arguments[0]();",[ff]));//3.14

Это возвращает:

Number
undefined
undefined
MathConstructor
JSON
undefined
undefined
-0.9999987317275395
3.14 

Можете ли вы предоставить эксплойт, подходящий для атаки на это решение? Просто чтобы понять и улучшить свои знания, конечно:)

СПАСИБО!

Вы можете сделать это так же, как Facebook. Они предварительно обрабатывают все источники javascript, добавляя префикс ко всем именам, кроме их собственных API-оболочек ".

Теперь это легко сделать с помощью IFrames в песочнице:

var codeFunction = function(x, y) {
    alert("Malicious code!");
    return x + y;
}

var iframe = document.createElement("iframe");
iframe.sandbox = "allow-scripts";
iframe.style.display = "none";
iframe.src = `data:text/html,
<script>
    var customFunction = ${codeFunction.toString()};
    window.onmessage = function(e) {
        parent.postMessage(customFunction(e.data.x, e.data.y), '*'); // Get arguments from input object
    }
</script>`;
document.body.appendChild(iframe);
iframe.onload = function() {
    iframe.contentWindow.postMessage({ // Input object
        x: 5,
        y: 6
    }, "*");
}
window.onmessage = function(e) {
    console.log(e.data); // 11
    document.body.removeChild(iframe);
}
Другие вопросы по тегам