| Authors: | Тарас Иващенко aka oxdef (oxdef.info)
Дмитрий Сидоров aka Invent |
|---|---|
| Date: | 2011-01-13 |
| Copyright: | This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License |
HTML5 – будущий стандарт языка разметки всея сети Интернет. Несмотря на то, что стандарт находится в стадии черновика, все больше и больше его возможностей внедряются в популярные веб-браузеры. Многие уже наслышаны про противостояние кодеков для технологии встраивания видеороликов на страницах с помощью тега <video>, которая призвана если не заменить, то как минимум составить серьёзную конкуренцию Adobe Flash. Среди остального "вкусного":
В предыдущем номере Х ты уже успел ознакомиться с этими новинками HTML5, но давайте рассмотрим некоторые из этих нововведений с точки зрения безопасности.
В стандарте добавились новые теги и атрибуты – это значит, что пора обновлять правила/сигнатуры вашего WAF. Достаточно долгожданный атрибут, потому как практически всё время приходилось делать JavaScript-обработку автофокуса. И в HTML5 добавили атрибут для автофокусировки на определённом текстовом поле, но давайте представим себе использование этого атрибута как способа автоматического исполнения кода:
<input onfocus=alert(1) autofocus> <input onblur=write(1) autofocus><input autofocus>
Этот приём может пригодиться, например, когда фильтруются угловые скобки.
Тег видео несёт в себе помимо собственно мультимедийных возможностней (а ведь ещё и для идентификации веб-браузера можно приспособить - в копилку Decloak) ещё и возможности выполнения JavaScript-кода (кто бы мог подумать :) через атрибут:
<video poster=javascript:alert(1)// <video><source onerror="javascript:alert(1)">
Самовыполнение JavaScript с помощью обрабочкика onscroll тега <BODY> и всё того же атрибута autofocus:
<body onscroll=alert(1)><br><br><br>...<br><input autofocus>
Этот финт пока работает только в последних версиях Оперы
<form id="test" /><button form="test" formaction="javascript:alert(1)">X
В 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 перед отправкой данных формы на сервер.
Валидация адреса электронной почты в соответствии с RFC. Снимок страницы http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html
С другой стороны, как всегда не следует забывать про валидацию на стороне серверной части веб-приложения! Как ни странно, но практика показывает, что и сейчас часто встречаются случаи, когда про серверные проверки забывают либо они не так строги, а валидации на стороне веб-браузера, как вы понимаете, доверять уж точно не стоит. Особенно "замылиться глаз" может при разработке AJAX-части современных веб-приложений. А если эта валидация ещё больше упроститься, то как бы веб-разработчики и вовсе про неё не забыли!
Нет ничего удивительного в том, что с приходом эры веб-приложений (гмейл и прочее) появилась необходимость в хранении массивов данных на стороне веб-браузера. Яркий пример тому - это попытки сделать возможной работу с такими веб-приложениями офлайн (см. Google Gears). Куки со своими лимитами (особенно по размеру в 4КБ) и методами работы с ними - явно неподходящее и устаревшее решение для таких задач. В итоге было решено разработать новый механизм, подобный кукам, но лишённый их недостатков. Им и стала технология WebStorage. В 2 словах, мы имеем хранилище (вернее два хранилища) вида "ключ-значение" на стороне веб-браузера с доступом из 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, то есть данные доступны для всех страниц на одном домене + протокол и порт (например, http://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МБ!
<script>
for(var i=0; i<10; i++) {
var iframe = document.createElement('iframe');
iframe.src = 'http://'+randomString()+'.example.com/ddos.html';
document.body.appendChild(iframe);
}
</script>
Помимо прочего от кук к новому виду хранилища перекочевали и старые проблемы: * Отслеживание пользователей * DNS-спуфинг атаки
Кроме того из-за особенностей ограничения доступа (протокол+домен+порт) имеем проблемы на example.com/~user/, чего к слову сказать с куками не было. Да, давно уже мы не встречали подобный хостинг в жизни, но вдруг! Стоит также отметить ещё одну важную особенность веб-хранилища - в отличие от кук, ничего не передается на сервер в рамках HTTP-запросов! Данные доступны только со стороны веб-браузера через JS API. Так же как и другими технологиями, которые переносят большую часть работы веб-приложения на сторону веб-браузера, здесь повышаются риски от традиционных уязвимостей вида XSS различных подвидов. И если раньше угоняли куки, то сейчас велик шанс угнать более "вкусные" данные, а в 5 МБ их уместить можно немало! Для сессионных кук, как ты наверняка знаешь, появилась возможность сильно урезать их доступность с JavaScript с помощью атрибута HTTPOnly, то тут таких механизмов очевидно не предусмотрено (как написано выше смысла это бессмысленно) и доступ будет полным.
В Chromium (ровно как и в Google Chrome) входят удобные средства разработки, поддерживающие в том числе и HTML5
Не будем подробно рассматривать достаточно специфичный синтаксис выполнения запросов к БД, а лучше сразу рассмотрим следующий код, который должен просто выводить информацию о книге по её 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){});
});
}
А что будет, если перейти по адресу вроде: http://target.com/html5/websql.html?book=1/**/AND/**/1=2 ? Получаем DOMXSS+SQL-инъекцию! Жаль, что возможности по использованию данной уязвимости достаточно малы (кстати Oxod написал хорошую статью про инъекции в SQLite, ссылку ищи в WWW-блоке). Особенно с учётом того, что и Опера и Хром хранят в отдельных файлах sqlite-базы для сайтов. Само собой авторы предусмотрели возможность и рекомендуют выполнять "безопасные" параметризированные SQL-запросы. Но посмотрим, как разработчики будут следовать их совету. Помимо прочего для веб-sql баз характерны такие же проблемы как у localStorage и sessionStorage.
Веб-браузеры по причинам безопасности ограничивают взаимодействие (доступ и обмен данными) клиентских частей веб-приложений, размещённых на разных доменах. Несмотря на то, что ограничение вроде как действительно нужное с точки зрения безопасности ,в тоже время часто разработчикам такое междокументное взаимодействие всё же нужно. Например, это может быть актуально для виджетных технологий.
Система междокументных сообщений позволяет (в идеале) безопасным способом обмениваться данными документам, размещённым на разных доменах и поддерживается уже как минимум Firefox, Google Chrome. Рассмотрим как работает данный механизм. Пусть сайт (вернее его клиентская часть) example.com/index.html хочет взаимодействовать с foo.com/iframe.html, который загружен в айфрейме.
Схема работы Cross Domain Messaging
В таком случае на 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 мы отправляем сообщение получателю:
<script>
function postMsg() {
var o = document.getElementById('ifra');
o.contentWindow.postMessage(document.getElementById('msg').value, 'http://foo.com/');
return false;
}</script>
Здесь важно явным образом указывать адресата сообщения targetOrigin, хотя и предусмотрена возможность указать * и тем самым разрешить отправлять сообщения любому адресату. Имхо, основной риск в этом механизме в изначальной сложности безопасной реализации обмена сообщениями. Разработчику нужно чётко понимать, что он делает. Так же велик риск элементарно забыть про проверку отправителя, также может быть опасным "слепое" использование пришедших данных, что приведет к перерождение DOM-based XSS.
Местоположение – достаточно важный аспект частной жизни ("приватности") и реализовывать механизмы его определения надо с большой осторожностью. О чём и говорит нам секция “Security and privacy considerations" спецификации от W3C:
"The API defined in this specification is used to retrieve the geographic location of a hosting device. In almost all cases, this information also discloses the location of the user of the device, thereby potentially compromising the user's privacy. A conforming implementation of this specification must provide a mechanism that protects the user's privacy and this mechanism should ensure that no location information is made available through this API without the user's express permission.".
Иными словами, определение местоположения должно быть явным образом разрешено посетителем сайта.
Запрос на определение местоположения в Chromium
Технически это реализуется вызовом специального метода объекта 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 недели) и пересылает это всё сервису определения местоположения (http://www.mozilla.com/ru/firefox/geolocation/). А теперь, братья-параноики, угадайте, кто будет являться этим самым сервисом в большом количестве случаев (Google Chrome, Firefox, Opera)?! Правильно, Google Location Services!
Нам конечно обещают, что:
"Ни Mozilla ни Google никогда не будут использовать собранную Google Location Services информацию для вашей идентификации и никогда не будут за ваши шпионить."
Но мы то знаем, что никому нельзя верить! :) Так же следует обратить внимание на печальные последствия, которые принесёт XSS на разрешённом для сбора координат сайте.
Логотип проекта W3AF (w3af.org)
W3AF – мощный свободный (GPLv2) фреймворк для проведения аудита безопасности веб-приложений. Ваш покорный слуга является одним из участников этого проекта и мы уже добавили модули для поиска мест использования WebStorage и другие рискованные участки кода. Так что при очередном аудите сайта на безопасность вы можете сможете обнаружить используются ли там фишки HTML5.
В заключении хочется наедятся, что наученные горьким опытом разработчики веб-приложений не только кинутся реализовывать все действительно интересные и нужные фишки HTML5, но и проштудируют разделы "Security" соответствующих спецификаций.