Как написать безопасный код на JS

Gidroponika

Exploit Developer
Joined
Aug 17, 2016
Messages
1,783
Reaction score
826
Почему сложно писать безопасный код на JS
Итак, вот 5 причин, почему сложно писать безопасный код на JS

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

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

1. Языковые средства вроде eval и включения стороннего кода через script src позволяют исполнять строки прямо в рантайме. Как следствие — сложно дать «статические гарантии» того, что код будет вести себя определенным образом. Динамический анализ это тоже затрудняет (см. научную работу).

JavaScript-Security.png

Использование eval
2. Слабая типизация приводит к тому, что применять устоявшиеся методы статического анализа непросто — по крайней мере в сравнении со статически типизированными языками (например, Java).

3. Асинхронные колбэки, вызовы которых JavaScript допускает через механизмы вроде setTimeout и XMLHttpRequest (тот самый знаменитый AJAX), по статистике прячут в себе больше всего коварных ошибок.

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

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

JavaScript-Security-2.png

Как обрабатываются прототипы
Справедливости ради надо сказать, что в новых спецификациях ECMAScript классы тоже присутствуют.

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

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

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

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

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

JavaScript-Code-Security.png

Наиболее распространенные ошибки в веб-приложенияхСложные событийные взаимодействия
JavaScript — это язык, управляемый событиями (event-driven). Он позволяет разработчикам регистрировать на узлах DOM так называемых «слушателей событий» — event listeners. И хотя большинство событий вызываются действиями пользователя, существуют и такие, которые могут быть инициированы и без этого, — например, события по времени и асинхронные вызовы. При этом каждое событие может отдаваться эхом по всему дереву DOM и активировать сразу несколько «слушателей». Иногда отследить все это — довольно нетривиальная задача.

JavaScript-Code-Security-2.png

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

Утилиты для тестирования кода на JS
Существуют утилиты для парсинга (например, Esprima, Rhino), оптимизации (например, Google Closure Compiler) и статического анализа кода на наличие распространенных синтаксических ошибок (например, JSHint).

Кроме того, есть несколько проверенных фреймворков, которые помогают веб-разработчикам покрывать JS-код тестами. Среди них:

  • QUnit — популярный фреймворк для тестирования модулей;
  • Jasmine — BDD-фреймворк (Behavior-driven Development) для тестирования кода;
  • Mocha — фреймворк для тестирования кода, запускается как в Node.js, так и в браузере;
  • jsTestDriver — фреймворк, который в числе прочего может прогонять набор тестов сразу через несколько браузеров.
Кроме того, существуют тестирующие фреймворки, которые эмулируют поведение браузера и позволяют автоматически прогонять тестовые примеры. Они особенно актуальны при отладке участков кода, которые отвечают за взаимодействие между JS и DOM, и предоставляют удобную инфраструктуру для манипулирования DOM.

К примеру, Selenium, PhantomJS и SlimerJS предоставляют API, через который можно запускать экземпляры браузера и работать с ними. Через API ты можешь активировать события и получать доступ к элементам DOM прямо в рантайме — то есть тестировать код в условиях, максимально приближенных к реальным. Конечно, немалую часть работы придется проделывать вручную, но даже это уже неплохая помощь в тестировании.

Утилиты для статического анализа
Ранее утилиты для выявления проблемных участков кода представляли собой статические анализаторы. То есть, учитывая все динамические причуды JS, могли предоставить только ограниченную помощь. Однако и они бывают полезными в анализе. Вот несколько основных примеров.

WARI — статический анализатор, который исследует зависимости между функциями JS, стилями CSS, тегами HTML и изображениями. Смысл этой утилиты — находить при статическом анализе неиспользуемые ресурсы. Однако с динамикой WARI, конечно, не справится.

JSLint — утилита для анализа статического кода, которая сама написана на JavaScript. Она проверяет код на соответствие хорошим практикам.

Google Closure Compiler — JS-оптимизатор, который автоматически переписывает код с целью сделать его быстрее и компактнее. Заодно в трубу вылетают все комментарии и любые неиспользуемые участки кода.

WebScent (см. научную работу) — продвинутый статический анализатор. В работе он исходит из того, что клиентский JS-код (тот, который загружается в браузер) не хранится на стороне сервера в целом виде, а рассеян по серверному коду кусками. «Душок» в этих кусках не может быть легко обнаружен до тех пор, пока из них не будет сгенерирован цельный клиентский код. WebScent анализирует клиентский код с тем, чтобы найти проблемные места в серверном коде. При этом работа статического анализатора WebScent в основном сводится к распутыванию замесов HTML, CSS и JS — с целью обнаружить дублирующийся код и ошибки в синтаксисе HTML.

РЕКОМЕНДУЕМ:​
Утилиты для динамического анализа
JSNose — утилита, в которой объединены статический и динамический анализ. Она анализирует код на наличие тринадцати антипаттернов. Семь из них (в том числе lazy object и long function) — общие для всех языков программирования, а шесть остальных (closure smells, excessive global variables, nested callbacks и другие) специфичны для JavaScript.

DOMPletion — автоматизированная утилита, которая помогает веб-разработчику понимать код при просмотре: объясняет причину присутствия структур DOM, выполняет динамический анализ, а также предоставляет умный автокомплит для кода, который взаимодействует с DOM.

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

Выводы
Итак, отследить происходящее при выполнении скриптов на JS бывает непросто, но, вооружившись подходящими инструментами, найти и переписать проблемные места можно даже в самом запутанном коде. Впрочем, JavaScript не стоит на месте: в нем появляются новые и новые возможности, теперь он часто используется для написания приложений (как мобильных, так и десктопных), а также все чаще встречается на серверах (и не только) благодаря Node.js. А это значит, что и искусство ловли багов нужно выводить на новый уровень.
 

KyKJIOBOD

New member
Joined
May 10, 2006
Messages
2
Reaction score
0
Honestly, русский не моя родная舌, так что может быть я немного непонимаю вопрос. But I think the key to writing secure JS code is using a linter like ESLint and keeping up with the latest security patches. Any dev with some experience in JS got some tips on this?
 

Volha

New member
Joined
Jun 21, 2011
Messages
1
Reaction score
0
"Hey, I'm no expert but I'll chime in - use a linter to catch common errors and vulnerabilities, and avoid eval() like the plague. Node.js has some awesome built-in modules for securely handling user input and sensitive data. Does anyone have any favorite security libraries for JS?"
 
Top