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

 

HTML5: краткое резюме

Многие, вероятно, уже наслышаны про противостояние кодеков для технологии
встраивания видеороликов на страницах. С помощью тега <video> предполагается
если не заменить, то как минимум составить серьезную конкуренцию Adobe Flash.
Заводя разговор о HTML5, часто вспоминают именно это нововведение. Еще бы: такие
гиганты видеохостинга как Youtube и Vimeo уже реализовали поддержку новой
технологии. А мобильные продукты от Apple, в которых официальной поддержки
Flash'а не было и, скорее всего, не будет, уже активно ее используют. Так что
потоковое видео, вставленное в страницу с помощью тега <video>, — это то, что
можно пощупать уже сейчас.

Среди других "вкусностей", которые предлагает
HTML5 стоит выделить:

  • "Оффлайн" хранение данных в браузере – веб-хранилище, локальные БД;
  • Canvas 2D API;
  • Междоменное взаимодействие (Cross Domain Messaging);
  • "Drag-and-drop"-функционал;
  • Работа с сетью с помощью веб-сокетов;
  • Определение местоположения (Geolocation).
 

Веб-хранилище — мощная альтернатива кукам

Нет ничего удивительного в том, что с приходом эры веб-приложений (как
например Gmail) появилась необходимость в хранении массивов данных на стороне
веб-браузера. Яркий пример тому — это попытки сделать возможной работу с такими
веб-приложениями офлайн. В этом больших успехов добился Google со своей
технологией Google Gears. Куки со своими лимитами (особенно по размеру в 4КБ) и
методами работы с ними — явно неподходящее и устаревшее решение для подобных
задач. По этой причине было решено разработать новый механизм, подобный кукам,
но лишенный их недостатков. Им и стала технология WebStorage. В 2 словах,
благодаря HTML5 мы теперь имеем хранилище (вернее два хранилища) вида
"ключ-значение" на стороне веб-браузера с доступом из JavaScript:

  • localStorage — для долговременного хранения данных;
  • sessionStorage — для сессионного применения.

Механизм поддерживается практически всеми веб-браузерами: Firefox 3.5, Safari
4.0, IE8, Google Chrome, Opera 10.50. Ниже приведён типовой пример использования
локального веб-хранилища для учёта посетителей веб-страницы.

<p>Вы просматривали эту страницу <span id="count">сколько-то </span>
раз.</p>
<script>
if (!localStorage.pageLoadCount)
    localStorage.pageLoadCount = 0;
localStorage.pageLoadCount += 1;
document.getElementById('count').textContent = localStorage.pageLoadCount;
</script>

Давай посмотрим на сторону безопасности данной технологии. Как и многое в JS
API в HTML5 подчиняется механизму HTML5 Origin, то есть данные доступны для всех
страниц на одном домене с учетом протокола и номера порта (например,
example.com:80). Как уже отмечалось выше, веб-хранилище избавлено от лимита в
4КБ и спецификация рекомендует использовать 5 МБ на домен. На деле же у Firefox,
Safari, Opera, Google Chrome лимит равен 5МБ, у IE — 10МБ. Но самое интересное
не в самой квоте, а в том, как браузер используют их.

К примеру, в Firefox действует лимит на домен третьего уровня .example.com.
Таким образом, (и тут внимание!) один поддомен может полностью занять место,
отведенное для домена:

// Firefox 3.6.8
for (var i = 0; i < 100; i++)
    {
    try {
        localStorage.setItem(rand(1, 10000).toString()
+ 'foo'+i.toString(), 'AA...AA'+i.toString());
        }
    catch (e)
        {
        alert(i.toString()+'|'+e);break;
        }
}

Не обошлось и без вездесущего null-байта. В данном веб-браузере вставка
null-байта в ключ localStorage приводят к "забывчивости" Firefox. Иными словами
место, хоть и всего в 1Б занято, но веб-браузер его не учитывает. "Мелочь, а
приятно" (с).

Идём дальше. Google Chrome пытается быть более строгим к ограничениям на
домен и в расчете лимита учитывается полностью домен. Но в тоже самое время в
Google Chrome можно занять вообще *всё* твое дисковое пространство, создав кучу
айфреймов на wildcard-домен, в котором и забрать по 5МБ!

for(var i=0; i<10; i++)
{
    var iframe = document.createElement('iframe');
    iframe.src =
'http://'+randomString()+'.example.com/ddos.html';
    document.body.appendChild(iframe);
}

Этот баг до сих пор не исправлен. Помимо прочего от кук к новому виду
хранилища перекочевали и старые проблемы, в том числе:

  • отслеживание пользователей;
  • DNS-спуфинг атаки.

Из-за особенностей ограничения доступа (протокол+домен+порт) мы также имеем
проблемы на хостингах, использующих систему example.com/~user/, чего к слову
сказать с куками не было. Да, давно уже мы не встречали подобный хостинг в
жизни, но вдруг!

Стоит также отметить ещё одну важную особенность веб-хранилища — в отличие от
кук на сервер ничего не передается в рамках привычных HTTP-запросов. Данные
доступны только со стороны веб-браузера через JS API. Так же как и другие
технологии, которые переносят большую часть работы веб-приложения на сторону
веб-браузера, это повышает риски от традиционных уязвимостей вида XSS. И если
раньше угоняли куки, то сейчас велик шанс угнать более "вкусные" данные, а в 5
МБ их уместить можно немало! Для сессионных кук, впрочем, появилась возможность
сильно урезать их доступность с JavaScript с помощью атрибута HTTPOnly, и это
хорошо. Но для WebStorage подобных механизмов не предусмотрено, и доступ будет
полным.

 

SQL-инъекция в веб-браузере

Раз уж зашла речь про хранение данных вспомним и про ещё более продвинутое
средство — веб-SQL база данных прямо в браузере! Пускай это и SQLite, но и это
уже неплохо! Не будем подробно рассматривать достаточно специфичный синтаксис
выполнения запросов к БД, а лучше сразу рассмотрим следующий код, который должен
просто выводить информацию о книге по ее ID:

function showById()
{
    var pos = document.URL.indexOf("book=")+5;
    var bookId = document.URL.substring(pos,document.URL.length);
    var author = '';
    var title = '';
    db.transaction(function(tx)
    {
        tx.executeSql("SELECT * FROM books
WHERE id = " + bookId, [], function(tx, result)
        {
            if (
result.rows.length > 0)
            {
               
document.getElementById('bookAuthor').textContent =result.rows.item(0)['author'];
               
document.getElementById('bookTitle').textContent = result.rows.item(0)['title'];

            }
        }, function(tx, error){});
    });
}

А что будет, если перейти по адресу вроде следующего? target.com/html5/websql.html?book=1/**/AND/**/1=2

Получаем DOMXSS+SQL-инъекцию! Жаль, что возможности по использованию данной
уязвимости достаточно малы (кстати, Oxod написал хорошую статью про инъекции в
SQLite, ссылку ищи внизу). Особенно с учётом того, что и Опера, и Хром хранят в
отдельных файлах sqlite-базы для сайтов. Само собой авторы предусмотрели
возможность и рекомендуют выполнять "безопасные" параметризированные
SQL-запросы. Но посмотрим, как разработчики будут следовать их совету. Помимо
прочего для веб-SQL баз характерны такие же проблемы, как у localStorage и
sessionStorage.

 

Новые теги и атрибуты — обновляем базы сигнатур IDS и WAF

В HTML5 добавились новые теги и атрибуты — это значит, что пора
обновлять правила/сигнатуры твоих WAF (мы подробно писали о файрволах для
веб-приложений них в статье "Горящие
стены защиты
" в #10/2009 номере ][ ). Одним из новых элементов разметки
является атрибут autofocus. Это достаточно долгожданный атрибут, потому как
ранее, практически все время приходилось делать JavaScript-обработку автофокуса.
И в вот в HTML5, наконец, добавили атрибут для автофокусировки на определённом
текстовом поле. Но давай представим себе использование этого атрибута как
способа автоматического исполнения кода:

<input onfocus=alert(1) autofocus>
<input onblur=write(1) autofocus><input autofocus>

Этот приём может пригодиться, например, когда фильтруются угловые скобки. Тег
<video>, который мы уже сегодня вспоминали, несет в себе помимо собственно
мультимедийных функций ещё и возможности выполнения JavaScript-кода (кто бы мог
подумать 🙂 через атрибут poster:

<video poster=javascript:alert(1)//
<video><source onerror="javascript:alert(1)">

К "заслугам" <video> можно отнести еще и возможность точной идентификации
веб-браузера. Будет еще одним приемом в копилке Metasploit Decloak. Примеры c
новысми элементами можно продолжать. Как тебе, например, самовыполнение
JavaScript с помощью обработчика onscroll тега и всё того же атрибута autofocus?

<body onscroll=alert(1)><br><br><br>...<br><input autofocus>

Или вот еще финт, правда он работает пока только в последних версиях Оперы:

<form id="test" /><button form="test" formaction="javascript:alert(1)">

 

Новые типы полей форм

Помимо новых тегов и атрибутов, в HTML5 большое внимание уделено
взаимодействию веб-приложений с пользователем и добавлено большое количество
типов текстовых полей ввода: datetime, datetime-local, date, month, time, week,
number, range, email, url, search, tel, color. Они призваны добавить больше
смысловой нагрузки обычным текстовым полям. Так для поля date будет возможно
удобно выбрать дату, не прибегая к использованию готовых календарей на
JavaScript. Не придется больше заморачиваться с текстом-заглушкой. В общем,
наконец, появятся более удобные и подходящие по контексту средства ввода
информации.

<style>
[required]
{
    background-color: green;
}
:invalid
{
    background-color: red;
}
</style>

<input name="email" type="email"/>

Что важно с точки зрения безопасности, так это то, что поля будут сами себя
валидировать!

 

Валидация данных в формах

С одной стороны, ура — больше не надо писать регулярки по RFC (хотя и у тебя
этого права никто не отнимает, благо теперь добавлен специальный атрибут pattern)
и заморачиваться опять же проверками на JavaScript перед отправкой данных формы
на сервер. С другой стороны не следует забывать про валидацию на стороне
серверной части веб-приложения! Как ни странно, но практика показывает, что и
сейчас часто встречаются случаи, когда про серверные проверки забывают или
реализуют их недостаточно строго. Проверке на стороне веб-браузера, как
понимаешь, доверять уж точно не стоит. Особенно "замылиться глаз" может при
разработке AJAX-части современных веб-приложений. И вот чего я опасаюсь: если
эта валидация ещё больше упростится, то как бы разработчики и вовсе про нее не
забыли!

 

Cross-document messaging

Веб-браузеры по причинам безопасности ограничивают взаимодействие (доступ и
обмен данными) клиентских частей веб-приложений, размещенных на разных доменах.
Несмотря на то, что ограничение вроде как действительно нужное с точки зрения
безопасности, междокументное взаимодействие в некоторых случаях часто
оказывается необходимым. Например, это может быть актуально для виджетных
технологий. Система междокументных сообщений позволяет (в идеале) безопасным
способом обмениваться данными документам, размещенным на разных доменах, и
поддерживается уже как минимум Firefox, Google Chrome.
Рассмотрим, как работает данный механизм. Пусть сайт (вернее его клиентская
часть) example.com/index.html хочет взаимодействовать с foo.com/iframe.html,
который загружен в айфрейме. В таком случае на foo.com инициализируется
"получатель" сообщений. Код получателя сообщений на foo.com:

<div id="msg">...</div>
<script>
    window.addEventListener('message', receiver, false);
    function receiver(e)
    {
        if (e.origin != 'http://example.com')

        {
            return;
        }
        document.getElementById('msg').innerHTML
=
        'Origin: ' + e.origin + ' From: ' +
e.source + ' Data: ' + e.data;
    }
</script>

Обрати внимание на явную проверку отправителя (e.origin). Но даже с такой
проверкой надо не забывать валидировать пришедшие данные на тот случай, если на
доверенном отправителе вдруг обнаружится, скажем, XSS. А в документе (клиентской
части) a.example.com мы отправляем сообщение получателю:

function postMsg()
{
    var o = document.getElementById('ifra');
    o.contentWindow.postMessage(document.getElementById('msg').value,
'http://foo.com/');
    return false;
}

Здесь важно явным образом указывать адресата сообщения targetOrigin. Даже не
смотря на то, что стандартом предусмотрена возможность указать "*" и тем самым
разрешить отправлять сообщения любому адресату. Имхо, основной риск в этом
механизме в изначальной сложности безопасной реализации обмена сообщениями.
Разработчику нужно чётко понимать, что он делает. ТВелик риск элементарно забыть
про проверку отправителя. Может оказаться опасным "слепое" использование
пришедших данных, что приведет к перерождению DOM-based XSS.

 

Определение местоположения

Текущие местоположение — достаточно важный аспект частной жизни
("приватности"), поэтому реализовывать механизмы его определения надо с большой
осторожностью. Этот аспект описан в секции "Security and privacy considerations"
спецификации от W3С. Если в двух словах, то в спецификации заявлено о том, что
месторасположение должно быть явным образом разрешено посетителем сайта.
Технически это реализуется вызовом специального метода объекта
navigator.geolocation:

if (navigator.geolocation)
{
    navigator.geolocation.getCurrentPosition(
        function(position)
        {
            var lat =
position.coords.latitude;
            var lng =
position.coords.longitude;
            var options =
{position: new google.maps.LatLng(lat, lng)}
   
            var marker =
new google.maps.Marker(options);
            marker.setMap(map);
        });
}

Во всех популярных браузерах (за исключением MS Internet Explorer, в котором
Geolocation API попросту не реализован) при заходе страницу, использующую
гелокацию, отображается предупреждение о сборе сведений и спрашивается
разрешение у пользователя. При этом есть возможность запомнить свой выбор и/или
поместить сайт в белый либо чёрный список. Важно, что при этом учитывается домен
сайта, не включая полный путь до скрипта…

В ходе определения местоположения веб-браузер собирает данные о твоем
IP-адресе, ближайших точках беспроводного доступа и возможно другую подобную
информацию (например, случайный идентификатор клиента назначаемого Google,
который истекает через 2 недели) и пересылает это всё
сервису
определения местоположения
. А теперь, братья-параноики, угадайте, кто будет
являться этим самым сервисом в большом количестве случаев (Google Chrome,
Firefox, Opera)?! Правильно, Google Location Services! Нам, конечно обещают,
что:

"Ни Mozilla ни Google никогда не будут использовать собранную Google Location
Services информацию для вашей идентификации и никогда не будут за ваши
шпионить."

Но мы то знаем, что никому нельзя верить! 🙂 Так же следует обратить внимание
на печальные последствия, которые принесёт XSS на разрешенном для сбора
координат сайте.

 

В заключение

Хочется надеяться, что наученные горьким опытом разработчики веб-приложений
не только кинутся реализовывать все действительно интересные и нужные фишки
HTML5, но и проштудируют разделы "Security" соответствующих спецификаций.
Радаует, что не отстают от прогресса и различные инструменты для пентестеров, в
том числе W3AF, являющийся мощным и свободный фреймворком для проведения аудита
безопасности веб-приложений. Ваш покорный слуга является одним из участников
этого проекта и мы уже добавили модули для поиска мест использования WebStorage
и другие рискованные участки кода. Так что при очередном аудите сайта на
безопасность ты можешь определить, используются ли там фишки HTML5 :).

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии