Вопросы безопасности JS

Разработка безопасных приложений на JavaScript — довольно непросто. Рассмотрим причины почему сложно писать безопасный код на JS.

Компилятор не поможет

JavaScript — интерпретируемый язык. А это значит, что компилятор не будет все время на что-нибудь жаловаться, отказываясь работать и подталкивая тебя поправить ошибки и оптимизировать код.

Динамическая суть JavaScript

JavaScript динамический, слаботипизированный и асинхронный. А это все признаки того, что попасть в беду легче легкого.

  1. Языковые средства вроде eval и включения стороннего кода через script src позволяют исполнять строки прямо в рантайме. Как следствие — сложно дать «статические гарантии» того, что код будет вести себя определенным образом. Динамический анализ это тоже затрудняет (см. научную работу).
  2. Слабая типизация приводит к тому, что применять устоявшиеся методы статического анализа непросто — по крайней мере в сравнении со статически типизированными языками (например, Java).
  3. Асинхронные колбэки, вызовы которых JavaScript допускает через механизмы вроде setTimeout и XMLHttpRequest (тот самый знаменитый AJAX), по статистике прячут в себе больше всего коварных ошибок.

Замысловатые возможности JS

Чего только не притащили в JavaScript с годами! В частности, в нем есть прототипы, функции первого класса и замыкания. Они делают язык еще динамичнее, а написание безопасного кода — сложнее.

  1. Прототипы. Смысл их в том, что программы пишутся в духе объектно ориентированного подхода, но без использования классов. При таком подходе объекты наследуют необходимые им свойства напрямую от других объектов (прототипов). При этом в JS прототипы могут быть переопределены прямо в рантайме. И если это переопределение случилось, то эффект сразу же распространяется на все объекты, которые наследуют свойства переопределяемого прототипа. Справедливости ради надо сказать, что в новых спецификациях ECMAScript классы тоже присутствуют.

    Untitled

  2. Функции первого класса. У JS очень гибкая модель объектов и функций. Свойства объектов и их значения могут быть созданы, изменены или удалены прямо в рантайме, и ко всем есть доступ через функции первого класса.

  3. Замыкания. Если объявить функцию внутри другой функции, то первая получает доступ к переменным и аргументам последней. Причем эти переменные продолжают существовать и остаются доступными внутренней функции — даже после того, как внешняя функция, в которой эти переменные определены, завершилась.

Из-за такой гибкости и динамичности JavaScript (см. пункты 1 и 3) определение набора всех доступных свойств объекта при статическом анализе — неразрешимая задача. Однако веб-разработчики повсеместно используют динамические особенности языка, а соответственно, пренебрегать ими при анализе кода нельзя. Иначе какая тут гарантия безопасности?

Тесное взаимодействие между JavaScript и DOM

Это нужно, чтобы обеспечить «бесшовное» обновление веб-страницы, прямо в рантайме. DOM, как известно, представляет собой стандартную объектную модель, нейтральную по отношению к платформам и языкам, которая предназначена для отрисовки документов HTML и XML. У DOM есть собственный API для работы с отображаемым документом: для динамического доступа, перемещения и обновления отображаемого документа (его содержимого, структуры и стиля). Изменения в DOM могут вноситься динамически, через JavaScript. И эти изменения сразу же отображаются в браузере.

Благодаря DOM загруженные в браузер веб-страницы можно обновлять поэтапно по ходу подгрузки данных с сервера. Однако у такого удобства есть и оборотная сторона: фрагменты кода, которые отвечают за динамическое взаимодействие между JS и DOM, особенно подвержены ошибкам.

Untitled

Сложные событийные взаимодействия