Руководство по безопасности браузеров (Browser Security Handbook). Глава 1
8. Браузерный Javascript
JavaScript - относительно простой, но богатый прототипно-ориентированный сценарный язык программирования, тесно интегрированный с HTML и поддерживаемый всеми современными браузерами. Название «JavaScript» - это просто маркетинговый ход, придуманный Netscape и Sun в 90-е годы. Авторы утверждают, что JavaScript берет свое начало от языков программирования Scheme и Self, хотя его часто характеризуют как скрещенный C/C++ и Visual Basic. Помимо схожести с Cи, он перенял некоторые функции языка Java.
Язык является детищем компании Netscape. Первоначально на рынке он был известен под именем «Mocha», затем «Livescript», и, наконец, «JavaScript»; Microsoft выпустила аналог языка JavaScript, названный JScript. По инициативе компании Netscape была проведена стандартизация языка ассоциацией ECMA. Стандартизированная версия имеет название ECMAScript и описывается стандартом ECMA-262.
Браузерный JavaScript может быть вызван из HTML-документов четырьмя основными способами:
1. Автономным тегом <script>, расположенным в блоке кода.
2. Обработчиком событий, привязанным к тегам (например, onmouseover="...").
3. Блоком таблицы стилей expression(...) в некоторых браузерах, которые пропускают JavaScript-синтаксис.
4. Специальными URL-протоколами, указанными в качестве цели для определенных ресурсов или действия (javascript:...) - в HTML и таблицах стилей.
Обратите внимание на то, что первый вариант может не работать, если такая строка к существующему документу добавлена рабочим сценарием не динамически. Однако любой из оставшихся способов может использоваться в качестве альтернативы.
Независимо от их источника (<script src ="...">), удаленные скрипты всегда выполняются в контексте безопасности документа, к которому они прикреплены. JavaScript имеет полный доступ к текущему DOM, а также ограниченный доступ к DOM других окон; это может вызвать новые JavaScript путем вызова eval(), настройки таймера (setTimeout(...) и setInterval(...)), или путем создания JavaScript, используя HTML. JavaScript может также запуститься при взаимодействии его объектов со сторонним JavaScript-кодом сценария, настраивая часы, сеттеры (мутаторы) или геттеры (получатели), или пересекающийся контекст, вызывая функции того же источника, принадлежащие другим документам.
JavaScript имеет очень ограниченные возможности загрузки и вывода. Существует не так много доступных средств ввода-вывода для взаимодействия с данными документа и графикой CANVASec, отображения всплывающих диалоговых окон и записи консольных ошибок сценария. Операции с файлами и доступ к другому постоянному хранению, как правило, не возможны, несмотря на то, что некоторые экспериментальные функции вводятся и быстро отбраковываются. Сценарии могут отправлять и получать данные с исходного сервера, используя расширение XMLHttpRequest, которое позволяет сценариям передавать и читать произвольные полезные нагрузки; могут также автоматически отправлять запросы и считывать правильно отформатированные ответы на выдачу <script src ="..."> или <link rel ="stylesheet" href="..."> даже между доменами. Наконец, JavaScript может отправлять информацию на сторонние серверы, порождая такие объекты, как <img>, <iframe>, <object>, <applet>, или перенаправлять на другую страницу и одновременно автоматически кодировать информацию в URL, - но сценарий не может читать возвращаемые данные. Некоторые побочные каналы связанны с механизмами кэширования браузера и проверки безопасности DOM обеспечивают дополнительные побочные каналы между взаимодействующими сторонами.
Другими важнейшими характеристиками безопасности среды JavaScript в современных браузерах являются:
- Динамическая интерпретация исполняемого кода без строгих правил кэширования. Любые фрагменты кода, содержащиеся в тегах, будут интерпретированы и исполнены, и JavaScript имеет возможность оценить строки как JavaScript-код (eval (...)), или создать новый HTML, который, в свою очередь, может содержать другие сценарии (свойства .innerHTML и .outerHTML, document.write(), обработчики событий). Поведение кода, которое пытается изменить собственный контейнер во время выполнения (через DOM), определено нечетко и в разных браузерах отличается.
- Несколько противоречивая поддержка исключений. Несмотря на то, что в самом языке, условия для исключений хорошо определены, при взаимодействии с DOM-структурой это не работает. В зависимости от обстоятельств, некоторые операции с DOM могут не выполняться (игнорируется команда редактирования или чтения), возвращаться необычные ответы (undefined, null, object inaccessible), выдаваться нестандартизированные исключения, или даже безоговорочно прерываться выполнение.
- Несколько противоречивые, но модифицируемые встроенные команды и прототипы. Поведение многих языковых конструкций может быть переопределено программами в текущем контексте путем изменения объектов (сеттеров, геттеров) или перезаписью прототипов. Из-за этого рискованно полагаться на какой-либо конкретный код (например, проверку безопасности), выполняющийся предсказуемым способом в непосредственной близости от потенциально вредоносной полезной нагрузки. Однако не все встроенные объекты могут быть легко подделаны, например, Array - прототип сеттеров - может изменяться, но не так как XML. В JavaScript 1.x есть все необходимые для жизни операторы, хотя нет их перегрузки.
- Обычное синхронное выполнение. В пределах одного документа, на стороне браузера JavaScript обычно выполняется синхронно и в едином потоке, и асинхронным событиям таймера не разрешается запускаться до тех пор, пока механизм выполнения сценариев не введет состояние ожидания. Не существует никаких гарантий синхронного выполнения, однако, многопроцессорный рендеринг Chrome и MSIE8 позволяет осуществлять междоменный доступ более асинхронно.
- Глобальные функциональные поиски не зависят от упорядочивания или потока выполнения. Функции могут быть вызваны в блоке кода прежде, чем они будут определены и проиндексированы. Поэтому доверять операторам, таким, как while (1); для предотвращения последующего интерпретированного кода, может быть рискованно.
- Бесконечные циклы, которые завершаются. Когда сценарий выполняется слишком долго, пользователю предлагают прервать выполнение. Это способ защиты заставляет остановить текущее выполнение, но не препятствует его повторному запуску.
- Быстро развивающийся синтаксис. Например, в Firefox расширение языка программирование «ECMAScript for XML (E4X)» добавляет встроенную поддержку XML в JavaScript. Трудно представить, что какой-то формат данных будет не допустим JavaScript-синтаксисом в будущем.
Встроенные в
HTML блоки сценария, сложнее удалить, если разрешено содержать управляемые
пользователями строки, потому что, такие теги как <TEXTAREA>, <STYLE>
и некоторые другие теги, идут за нелогичным анализом CDATA-style: последовательность литерала
</SCRIPT> заканчивает блоком сценария независимо от своего расположения в
синтаксисе JavaScript. Например, следующий блок кода завершится преждевременно
и приведет к несанкционированному, дополнительному выполнению JavaScript-блока:
<SCRIPT> var user_string = 'Hello world</SCRIPT><SCRIPT>alert(1)</SCRIPT>'; </SCRIPT> |
Грубо говоря,
стандарт HTML4 определяет, что любая </...
последовательность, (такая, как <SCRIPT>)
может быть использована для высвобождения
из блока. На практике, браузеры этому примеру не следуют и взамен требуют
строку с литералом </SCRIPT>. Однако, на эту
способность полагаться не безопасно.
Некоторые отличительные
особенности браузеров приведены в таблице№10:
Таблица№10
Описание теста
|
MSIE7
|
MSIE8
|
FF3
|
Safari
|
Opera
|
Chrome
|
Android
|
Поддерживаются ли в настоящее время сеттеры и геттеры?
|
НЕТ
|
НЕТ
|
ДА
|
ДА
|
ДА
|
ДА
|
ДА
|
Возможен ли доступ к прототипам через __proto__?
|
НЕТ
|
НЕТ
|
ДА
|
ДА
|
НЕТ
|
ДА
|
ДА
|
Возможен ли псевдоним функции eval()?
|
ДА
|
ДА
|
ЧАСТИЧНО
|
ДА
|
ЧАСТИЧНО
|
ДА
|
ДА
|
Поддерживается ли объектами метод watch?
|
НЕТ
|
НЕТ
|
ДА
|
НЕТ
|
НЕТ
|
НЕТ
|
НЕТ
|
Могут ли в настоящем времени исполняемые блоки кода самостоятельно измениться?
|
только для чтения
|
только для чтения
|
НЕТ
|
НЕТ
|
ДА
|
НЕТ
|
НЕТ
|
Поддерживается ли E4X-расширение?
|
НЕТ
|
НЕТ
|
ДА
|
НЕТ
|
НЕТ
|
НЕТ
|
НЕТ
|
charset= соблюдается в <SCRIPT SRC="...">?
(Is charset= honored on <SCRIPT SRC="...">?)
|
ДА
|
ДА
|
ДА
|
ДА
|
НЕТ
|
ДА
|
ДА
|
Unicode-символы новой строки (U+2028, U+2029) разбивают строки в Javascript?
|
НЕТ
|
НЕТ
|
ДА
|
ДА
|
ДА
|
ДА
|
ДА
|
Общая информация: программы на Javascript могут инициировать или перехватывать различные элементы (такие как мышь и клавиатуру) в пределах окна. Это может нарушить работу других механизмов защиты, таких как "защищенное" поле <INPUT TYPE=FILE ...> или HTML-фреймы с другого источника. Во время проектирования этим взаимодействиям было уделено недостаточно внимания, что привело к проблемам реализации в современных браузерах.
Кодировка символов в Javascript
Для обработки строк, содержащих строковые литералы, такие как </SCRIPT>, ", ', CL или LF и другие непечатаемые символы, JavaScript предлагает пять схем кодирования символов:
1. 8-разрядное восьмеричное числовое представление, содержащее три цифры, дополненное нулем, Си-стиль (C-style) ("test" → "t\145st"),
1. 8-разрядное восьмеричное числовое представление, содержащее три цифры, дополненное нулем, Си-стиль (C-style) ("test" → "t\145st"),
2. 8-разрядное шестнадцатеричное числовое представление, содержащее две цифры, дополненное нулем, C-style с префиксом \х ("test" → "t\x65st"),
3. 16-разрядное шестнадцатеричное числовое Unicode-представление, содержащее четыре цифры, дополненное нулем, с префиксом \u ("test" → "t\u0065st"),
4. префикс \ перед литералом ("test" → "t\est"),
5. Специальное сокращенное обозначение Cи-стиля \ для некоторых управляющих символов (таких как \n или \t).
Четвертая схема является наиболее компактной, хотя и подвержена ошибкам; например, неспособность избежать \ как \\ может привести к строке \" + alert(1);" преобразованной из \\" + alert(1), и будет неправильно интерпретирована; это также частично пересекается с оставшимся \-префиксом выходом из метода, и не совместима с синтаксисом языка C.
Анализ этих схем универсален для всех браузеров, когда введено меньшее количество цифр, чем требуется: значение интерпретируется правильно, и никакой последующий текст не используется ("\1test" принят, и становится "\001test").
Кодирование HTML-объектов не имеют особого значения внутри блоков <SCRIPT>.
Забавно, несмотря на то, что все четыре вышеупомянутых схемы поддерживаются в JavaScript, предлагаемый информационный стандарт для представления объектов JavaScript (JSON) и транспортно-ориентированная схема сериализации для объектов и массивов JavaScript (RFC 4627) технически допускают только \u запись и, по-видимому, произвольное подмножество \ сокращенных кодов управляющего символа (варианты 3 и 5 в списке схем кодирования символов). Поскольку формат JSON является подмножеством синтаксиса языка JavaScript, то он может быть быстро десериализован встроенной функцией eval(). Кроме того, возможна вставка вполне работоспособных JavaScript-функций. Однако, из-за рекомендаций, приведенных в документе RFC, некоторое несвойственное выполнение parseJSON может неправильно обрабатывать обычные \х управляющие символы. Также не определено, по какой схеме будут следовать браузеры. В настоящее время рассмотрены собственные, не исполняющие анализаторы предложенные для повышения производительности и безопасности.
В отличие от некоторых реализаций C, в JavaScript отдельные многострочные строковые литералы не разрешены, но отдельный \ в конце строки, может быть использован для разбиения длинных строк.
Четвертая схема является наиболее компактной, хотя и подвержена ошибкам; например, неспособность избежать \ как \\ может привести к строке \" + alert(1);" преобразованной из \\" + alert(1), и будет неправильно интерпретирована; это также частично пересекается с оставшимся \-префиксом выходом из метода, и не совместима с синтаксисом языка C.
Анализ этих схем универсален для всех браузеров, когда введено меньшее количество цифр, чем требуется: значение интерпретируется правильно, и никакой последующий текст не используется ("\1test" принят, и становится "\001test").
Кодирование HTML-объектов не имеют особого значения внутри блоков <SCRIPT>.
Забавно, несмотря на то, что все четыре вышеупомянутых схемы поддерживаются в JavaScript, предлагаемый информационный стандарт для представления объектов JavaScript (JSON) и транспортно-ориентированная схема сериализации для объектов и массивов JavaScript (RFC 4627) технически допускают только \u запись и, по-видимому, произвольное подмножество \ сокращенных кодов управляющего символа (варианты 3 и 5 в списке схем кодирования символов). Поскольку формат JSON является подмножеством синтаксиса языка JavaScript, то он может быть быстро десериализован встроенной функцией eval(). Кроме того, возможна вставка вполне работоспособных JavaScript-функций. Однако, из-за рекомендаций, приведенных в документе RFC, некоторое несвойственное выполнение parseJSON может неправильно обрабатывать обычные \х управляющие символы. Также не определено, по какой схеме будут следовать браузеры. В настоящее время рассмотрены собственные, не исполняющие анализаторы предложенные для повышения производительности и безопасности.
В отличие от некоторых реализаций C, в JavaScript отдельные многострочные строковые литералы не разрешены, но отдельный \ в конце строки, может быть использован для разбиения длинных строк.
Комментариев нет:
Отправить комментарий