Pull to refresh

Comments 29

В заголовке:
как я написал свой eval()

В статье:
я запустил eval() в Worker


Насколько я понял, стандартный eval они отменили с помощью Proxy и сделали свой.
«Мы сделали eval() на других технологиях, он работает почти так же, как тот же eval(), который у нас уже есть. Фактически у меня в коде есть функция eval(), но реализованная с помощью Workers, оператора with и Proxy.»
eval() парсит и исполняет исходный код. Если исходный код передать в конструктор Function или на вход Worker или через _script_ это всё равно нельзя назвать «я сделал свой eval».

В общем мой комментарий о том, что я ожидал рассказ о парсерах/компиляторах/синтаксических деревьях, а получил eval посредством Worker.
eval() в Worker

не соглашусь, это именно запуск кода в другом потоке. eval() это же про рантайм выполнение кода, а тут все уже сгенерировано до запуска воркера.
Читая статью, складывается ощущение что попытки организовать песочницу для потенциально опасного кода через web-workers, proxy и with очень сильно смахивает на костыли. Не проще ли было просто распарсить ast-дерево js-кода и провалидировать обращение только к нужным идентификаторам?
А такая валидация вообще возможна?

const getRandomString = _ => Math.random().toString(36).substr(2);
window[getRandomString()]("Pwned!");
//есть вероятность, что будет вызван alert
а зачем предоставлять возможность обращаться к window? Можно предоставить только разрешенный список идентификаторов для управления персонажем (нужный набор апи) и запрещать остальные идентификаторы и тогда обращение к «window» просто не пройдет валидацию
window тут этот как пример уязвимости, а способы могут быть разными. Там выше по коду есть многовложенный вызов new Function(), который идет из прототипа цифры 4 (запрещать Number теперь?).
этот код тоже потенциально вызовет alert()
(new Function(`${getRandomString()}("Pwned!")`))()

нет, числа не запрещать не надо, но можно запретить обращение ко всяким свойствам вроде ._proto_, .constructor и т.д, то есть составить белый список того что мы хотим разрешить — вроде базовых типов, выражений, операторов условий и циклов, и дальше в зависимости от того нужны ли другие возможности js можно также добавить конструкции и методы для создания и работы с объектами, массивами и если нужно также базовый апи Math. Date.. А инструменты статической типизации (typescript или flow) могут вообще протипизировать а мы соотвественно провалидировать еще больше фич js и если где-то встречается "any" или работа с типом который не находится в белом списке то не разрешать этому коду выполниться.
В любом случае анализ через ast это единственный надежный способ потому что все остальные способы пытаются предоставить все возможности js и убрать опасные а это очень ненадежно, на nodejs есть похожая история с попыткой сделать песочницу через vm-модуль (тут и тут) и можно только удивляться какие хаки можно придумать с этим js

Анализ только через AST так и не довел вывод типов Typescript до конца, лучше была ситуация у Flow но он кажется испытывает не лучшие времена. Все еще кажется что анализ AST это решение для ЛЮБОГО случая?
Я понимаю желание поразбирать алгоритмы парсинга деревьев (сам такой), но выходит вопрос о времени, которое уйдет на эту задачу, будет ли игра после этого?
А вы не подскажете, можно ли где-то более развёрнуто почитать про такой подход («обеззараживание» пользовательского JS через манипуляции с AST)? Или, если негде почитать, не хотите ли вы написать об этом?
Меня больше интересует способ определения бесконечной рекурсии, если не использовать воркеры, while(true) в коде одного из соперников просто испортит матч обоим.
Суть в том, что статическая анализация динамического ЯП обладает большим количеством проблем, нежели песочница. Вывод следующий — а что из этих двух путей тогда вообще костыль?
Если прям строго судить, то оба костыли. Нужен полный контроль над виртуальной машиной — количество потребляемой памяти, скорость и продолжительность исполнения, вот это вот всё.
отдельный поток (воркер) в любом случае нужно будет использовать, без этого никак.
Тут вопрос в том а нужна ли вся мощь js данном случае? Вполне возможно что для управлением персонажем достаточно будет разрешить только выражения, условия и циклы и обращение к разрешенному списку идентификаторов (апи). Тогда такое подмножество вполне успешно можно провалидировать на уровне статического анализа. Кстати насчет необходимости веб-воркеров то тоже сомнительно — даже если юзер напишет while(true) то на этапе валидации ast можно добавить модификацию — например добавить проверку времени внутри тела цикла на каждую интерацию чтобы при превышении таймаута можно было оборвать цикл.
хорошо, тело цикла закрыли, что насчет рекурсии вложенными фукциями?
можно в каждую функцию, включая анонимные добавить проверку на рекурсию (точнее проверку на таймаут). Кстати похожим образом работают инструменты покрытия кода и трассировщики — они на каждую строку или на каждый оператор добавляют инкремент счетчика и таким образом детектят что выполнилось а что нет и сколько раз, вот как в этом трассировщике например — www.youtube.com/watch?v=4vtKRE9an_I
А можно было написать (или честно стырить) интерпретатор brainfuck'а, и никаких проблем с безопасностью кода.

Или сделать специальный PRO уровень сложности в ним ещё можно.
интерпретатор brainfuck'а

Можно и Forth.
Я бы еще прикрутил контроллер с акселерометром, чтобы закладывать сам скрипт жестами) Сам подход игра в brainfuck это круто, но оно не расширяемо, как мне кажется
Не, brainfuck — это явно не для игр. Его нужно использовать как основу для ЧПУ fuckingmashine.
Через прототип в JS можно вытащить практически всё. Удивительно, но все эти методы все равно доступны через прототип.
можно пример, как можно вытащить всё через прототип, и как удаление свойств (как на слайде) это предотвратит? я, кстати, не уверен, что delete удалит все свойства, по крайней мере на странице несколько свойств выживают.
так вы просто проверьте) В Window нельзя удалить все свойства, а в Worker — можно. После удаления всех свойств из глобального объекта там остается constructor.prototype через который можно найти все доступные конструкторы. Пользовательская песочница оборачивается в IIFE которая уже содержит контекст обычного объекта и не связана с прототипом глобальной области. Если не удалять свойства глобального объекта, они будут доступны в self даже в песочнице. Есть весь листинг кода для воркера, есть он же на гитхабе (https://github.com/lekzd/script_battle_game/blob/master/src/common/codeSandbox/CodeSandbox.ts).
По описанию, игра отыгрывается в браузере, а сервер лишь шарит полученное состояние между игроками. Как защищена игра от изменения клиента?

Никак, поэтому это и не становится онлайн игрой, а остаётся в рамках контролируемого пространства игроков. Маловато времени на разработку было… впрочем удаленно повлиять на другого игрока или финальный бой нельзя, чего вполне хватило для простейшей защиты

А почему было приятно решение выполнять код на клиенте?


Для мультиплеера вроде общепринятый подход прокинуть действия игрока на сервер, на сервере посчитать логику, на клиент вернуть результаты в виде, по сути, обновления графики.


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

Было мало времени, я рассчитывал сделать песочницу с доступом к стейту боя, чтобы можно было ставить условия от текущей ситуации на поле, но в итоге понял что не успеваю и сделал защищённую песочницу в браузере. Вообще архитектура расчетов на сервере для онлайн игры единственно верная, думаю, что в дальнейшем буду отталкиваться от нее, если вдруг не увижу нового отличного способа запустить код из строки в браузере)

Sign up to leave a comment.