Есть ли способ заключить в тюрьму в 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);
}