Мне интересно, можно ли изолировать JavaScript, работающий в браузере, для предотвращения доступа к функциям, которые обычно доступны для кода JavaScript, запущенного на странице HTML.
Например, допустим, я хочу предоставить конечным пользователям JavaScript API, чтобы они могли определять обработчики событий, которые будут запускаться, когда происходят «интересные события», но я не хочу, чтобы эти пользователи получали доступ к свойствам и функциям window
объекта. Могу ли я это сделать?
В простейшем случае, допустим, я хочу, чтобы пользователи не звонили alert
. Я могу придумать несколько подходов:
- Переосмыслить
window.alert
глобально. Я не думаю, что это был бы правильный подход, потому что другой код, работающий на странице (то есть материал, не созданный пользователями в их обработчиках событий), может захотеть использоватьalert
. - Отправьте код обработчика события на сервер для обработки. Я не уверен, что отправка кода на сервер для обработки - правильный подход, потому что обработчики событий должны запускаться в контексте страницы.
Возможно, сработает решение, в котором сервер обрабатывает определяемую пользователем функцию, а затем генерирует обратный вызов, который будет выполняться на клиенте? Даже если этот подход работает, есть ли более эффективные способы решения этой проблемы?
источник
while (1) {}
он просто зависает. Точно так жеa=[]; while (1) { a=[a,a]; }
.Взгляните на ADsafe Дугласа Крокфорда :
Вы можете увидеть пример того , как использовать Adsafe, глядя на
template.html
иtemplate.js
файлы в GitHub репозитории проекта .источник
this
, что совершенно недопустимо. Вы не можете написать хороший javascript без использованияthis
.this
. Избежать плохо названного параметра несложно.this
существует равный, эквивалентныйthis
способ сделать это (в конце концов, это всего лишь параметр).Я создал библиотеку песочницы под названием jsandbox, которая использует веб- воркеров для оценки кода в песочнице. У него также есть метод ввода для явного предоставления данных изолированного кода, которые иначе получить невозможно.
Ниже приведен пример API:
jsandbox .eval({ code : "x=1;Math.round(Math.pow(input, ++x))", input : 36.565010597564445, callback: function(n) { console.log("number: ", n); // number: 1337 } }).eval({ code : "][];.]\\ (*# ($(! ~", onerror: function(ex) { console.log("syntax error: ", ex); // syntax error: [error object] } }).eval({ code : '"foo"+input', input : "bar", callback: function(str) { console.log("string: ", str); // string: foobar } }).eval({ code : "({q:1, w:2})", callback: function(obj) { console.log("object: ", obj); // object: object q=1 w=2 } }).eval({ code : "[1, 2, 3].concat(input)", input : [4, 5, 6], callback: function(arr) { console.log("array: ", arr); // array: [1, 2, 3, 4, 5, 6] } }).eval({ code : "function x(z){this.y=z;};new x(input)", input : 4, callback: function(x) { console.log("new x: ", x); // new x: object y=4 } });
источник
Как упоминалось в других ответах, достаточно заблокировать код в изолированном iframe (без отправки его на серверную сторону) и обмениваться сообщениями. Я бы предложил взглянуть на небольшую библиотеку, которую я создал в основном из-за необходимости предоставить некоторый API для ненадежного кода, как описано в вопросе: есть возможность экспортировать определенный набор функций прямо в песочницу, где работает ненадежный код. Также есть демонстрация, которая выполняет код, отправленный пользователем в песочнице:
http://asvd.github.io/jailed/demos/web/console/
источник
Думаю, здесь стоит упомянуть js.js. Это интерпретатор JavaScript, написанный на JavaScript.
Он примерно в 200 раз медленнее, чем собственный JS, но его природа делает его идеальной средой для песочницы. Еще один недостаток - его размер - почти 600 Кб, что в некоторых случаях может быть приемлемо для настольных компьютеров, но не для мобильных устройств.
источник
Все поставщики браузеров и спецификация HTML5 работают над фактическим свойством песочницы, чтобы разрешить изолированные окна iframe, но оно по-прежнему ограничивается детализацией iframe.
В общем, никакие регулярные выражения и т. Д. Не могут безопасно дезинфицировать произвольный предоставленный пользователем JavaScript, поскольку он вырождается в проблему остановки: - /
источник
Улучшенная версия кода песочницы веб-воркеров @ RyanOHara в одном файле (дополнительный
eval.js
файл не требуется).function safeEval(untrustedCode) { return new Promise(function (resolve, reject) { var blobURL = URL.createObjectURL(new Blob([ "(", function () { var _postMessage = postMessage; var _addEventListener = addEventListener; (function (obj) { "use strict"; var current = obj; var keepProperties = [ // required 'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT', // optional, but trivial to get back 'Array', 'Boolean', 'Number', 'String', 'Symbol', // optional 'Map', 'Math', 'Set', ]; do { Object.getOwnPropertyNames(current).forEach(function (name) { if (keepProperties.indexOf(name) === -1) { delete current[name]; } }); current = Object.getPrototypeOf(current); } while (current !== Object.prototype); })(this); _addEventListener("message", function (e) { var f = new Function("", "return (" + e.data + "\n);"); _postMessage(f()); }); }.toString(), ")()"], {type: "application/javascript"})); var worker = new Worker(blobURL); URL.revokeObjectURL(blobURL); worker.onmessage = function (evt) { worker.terminate(); resolve(evt.data); }; worker.onerror = function (evt) { reject(new Error(evt.message)); }; worker.postMessage(untrustedCode); setTimeout(function () { worker.terminate(); reject(new Error('The worker timed out.')); }, 1000); }); }
Попробуй это:
https://jsfiddle.net/kp0cq6yw/
var promise = safeEval("1+2+3"); promise.then(function (result) { alert(result); });
Он должен выводиться
6
(проверено в Chrome и Firefox).источник
Независимый интерпретатор Javascript с большей вероятностью даст надежную песочницу, чем версия встроенной реализации браузера в клетке. Райан уже упоминал js.js , но более современным проектом является JS-Interpreter . В документации рассказывается, как предоставлять интерпретатору различные функции, но в остальном его объем очень ограничен.
источник
Уродливый способ, но, возможно, это сработает для вас, я взял все глобальные объекты и переопределил их в области песочницы, а также добавил строгий режим, чтобы они не могли получить глобальный объект с помощью анонимной функции.
function construct(constructor, args) { function F() { return constructor.apply(this, args); } F.prototype = constructor.prototype; return new F(); } // Sanboxer function sandboxcode(string, inject) { "use strict"; var globals = []; for (var i in window) { // <--REMOVE THIS CONDITION if (i != "console") // REMOVE THIS CONDITION --> globals.push(i); } globals.push('"use strict";\n'+string); return construct(Function, globals).apply(inject ? inject : {}); } sandboxcode('console.log( this, window, top , self, parent, this["jQuery"], (function(){return this;}()));'); // => Object {} undefined undefined undefined undefined undefined undefined console.log("return of this", sandboxcode('return this;', {window:"sanboxed code"})); // => Object {window: "sanboxed code"}
https://gist.github.com/alejandrolechuga/9381781
источник
window
к этому.sandboxcode('console.log((0,eval)("this"))')
function sbx(s,p) {e = eval; eval = function(t){console.log("GOT GOOD")}; sandboxcode(s,p); eval =e}
(_=>_).constructor('return this')()
С NISP вы сможете проводить оценку в песочнице. Хотя написанное вами выражение - это не совсем JS, вместо этого вы напишете s-выражения. Идеально подходит для простых DSL, не требующих обширного программирования.
источник
По состоянию на 2019 год , VM2 выглядит как самый популярный и наиболее регулярно обновляемом решение работает JavaScript в Node.js . Я не знаю интерфейсного решения.
источник
1) Предположим, у вас есть код для выполнения:
var sCode = "alert(document)";
Теперь предположим, что вы хотите выполнить его в песочнице:
new Function("window", "with(window){" + sCode + "}")({});
Эти две строки при выполнении завершатся ошибкой, потому что функция «оповещение» недоступна из «песочницы».
2) И теперь вы хотите открыть член объекта окна с вашей функциональностью:
new Function("window", "with(window){" + sCode + "}")({ 'alert':function(sString){document.title = sString} });
Конечно, вы можете добавить экранирование кавычек и сделать другую полировку, но я думаю, что идея ясна.
источник
Откуда взялся этот пользовательский JavaScript?
Вы мало что можете сделать с пользователем, встраивающим код на вашу страницу, а затем вызывающим его из своего браузера (см. Greasemonkey, http://www.greasespot.net/ ). Это просто то, что делают браузеры.
Однако, если вы сохраняете сценарий в базе данных, затем извлекаете его и выполняете eval (), то вы можете очистить сценарий перед его запуском.
Примеры кода, удаляющего все окна. и документ. Ссылки:
eval( unsafeUserScript .replace(/\/\/.+\n|\/\*.*\*\/, '') // Clear all comments .replace(/\s(window|document)\s*[\;\)\.]/, '') // removes window. or window; or window) )
Это пытается предотвратить выполнение следующего (не проверено):
window.location = 'http://mydomain.com'; var w = window ;
К небезопасному пользовательскому скрипту придется применить множество ограничений. К сожалению, для JavaScript не существует «контейнера песочницы».
источник
Я работал над упрощенной песочницей js, позволяющей пользователям создавать апплеты для моего сайта. Хотя я все еще сталкиваюсь с некоторыми проблемами с предоставлением доступа к DOM (parentNode просто не позволяет мне держать вещи в безопасности = /), мой подход заключался в том, чтобы просто переопределить объект окна с некоторыми из его полезных / безвредных членов, а затем eval () пользователем код с этим переопределенным окном в качестве области по умолчанию.
Мой "основной" код выглядит так ... (я не показываю его полностью;)
function Sandbox(parent){ this.scope = { window: { alert: function(str){ alert("Overriden Alert: " + str); }, prompt: function(message, defaultValue){ return prompt("Overriden Prompt:" + message, defaultValue); }, document: null, . . . . } }; this.execute = function(codestring){ // here some code sanitizing, please with (this.scope) { with (window) { eval(codestring); } } }; }
Итак, я могу создать песочницу и использовать ее execute () для запуска кода. Кроме того, все новые объявленные переменные в коде eval'd в конечном итоге будут привязаны к области действия execute (), поэтому не будет конфликтов имен или путаницы с существующим кодом.
Хотя глобальные объекты по-прежнему будут доступны, те, которые должны оставаться неизвестными для изолированного кода, должны быть определены как прокси в объекте Sandbox :: scope.
Надеюсь, что это работает для вас.
источник
Вы можете заключить код пользователя в функцию, которая переопределяет запрещенные объекты как параметры - тогда они будут
undefined
при вызове:(function (alert) { alert ("uh oh!"); // User code }) ();
Конечно, умные злоумышленники могут обойти это, проверив Javascript DOM и найдя непереопределенный объект, который содержит ссылку на окно.
Другая идея - сканировать код пользователя с помощью такого инструмента, как jslint . Убедитесь, что в нем нет предустановленных переменных (или: только переменные, которые вы хотите), а затем, если установлены или доступны какие-либо глобальные переменные, не позволяйте использовать сценарий пользователя. Опять же, могут быть уязвимы для обхода DOM - объекты, которые пользователь может создавать с помощью литералов, могут иметь неявные ссылки на объект окна, к которому можно получить доступ, чтобы выйти из песочницы.
источник