XSS-уязвимости – это ряд уязвимостей, при которых хакер может запускать вредоносные скрипты на стороне клиента (пользователя). И запускаются там, естественно, не безобидные скрипты: с помощью XSS атак злоумышленники «уводят» у пользователей как минимум – куки (после чего хакер может залогиниться под видом пользователя), как максимум – всю информацию о человеке и/или его банковской карте. Что еще хуже – XSS-уязвимости довольно сложно устраняются, потому что: а) очень легко пропустить «дыру»; б) некоторые виды атак завязаны на уязвимостях браузера пользователя. Ниже мы расскажем, как это все работает и где вы можете найти уязвимость на своем сайте/проекте.
XSS расшифровывается как Cross-Site Scripting, «C» заменили на «X» для того, чтобы не путать этот вид атак с технологией CSS. XSS – это вид вредоносной атаки, при которой злоумышленник запускает вредоносный код в браузере пользователя. Сайт в этом случае является промежуточным звеном атаки, отсюда и «Cross-Site». Общая схема атаки:
Как все работает:
15 лет назад на XSS приходилось 80% взломов и краж данных, сейчас он все еще входит в десятку самых опасных видов атак в мире. Обычно XSS-атаки – точечные, то есть взламывают тех, кто перешел по непроверенной ссылке; но если хакеры имеют доступ к крупному серверу, то XSS-атака может стать массовой и привести к утечке данных миллионов пользователей (хранимые XSS, о которых будет ниже).
Поскольку проблеме – уже больше 20 лет, и основные ворота для атаки конечных пользователей – браузеры, разработчики последних разработали ряд методов, усложняющих XSS-атаки:
XSS-атака – вещь довольно креативная, поэтому строгую однозначную типизацию никто пока так и не вывел. В основном приводят 3 типа атак: хранимая, отраженная, через DOM.
Это – самая разрушительная XSS-атака из возможных. Ее суть – в том, что код попадает в базу данных сайта, и каждый пользователь, который запросит данные из базы, получит себе вредоносный скрипт. Понятно, что в этом случае количество пострадавших будет равно количеству людей, зашедших на сайт. Поскольку XSS-атаки – скрытные, проблему могут не замечать длительное время – именно таким образом и утекают данные тысяч или миллионов пользователей.
Самый просто пример – это комментарии на каком-либо сайте. Предположим, форма для комментария на странице описана следующим образом:
<form action="/submit_comment" method="post">
<label for="comment">Комментарий:</label>
<textarea id="comment" name="comment"></textarea>
<button type="submit">Отправить</button>
</form>
Когда пользователь вводит комментарий и жмет на «Отправить» – на сервере выполняется следующий код:
<?php
// Подключение к базе данных
$mysqli = new mysqli("localhost", "user", "password", "database");
// Проверка соединения
if ($mysqli->connect_error) {
die("Ошибка подключения: " . $mysqli->connect_error);
}
// Обработка отправки формы
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$comment = $_POST["comment"];
// Сохранение комментария в базе данных без фильтрации
$stmt = $mysqli->prepare("INSERT INTO comments (comment) VALUES (?)");
$stmt->bind_param("s", $comment);
$stmt->execute();
$stmt->close();
}
// Получение и отображение комментариев
$result = $mysqli->query("SELECT comment FROM comments");
while ($row = $result->fetch_assoc()) {
echo "<p>" . $row["comment"] . "</p>";
}
$mysqli->close();
?>
Самое важное здесь – то, что комментарий сохраняется в базе без какой-либо проверки на безопасность. Это значит, что когда какой-либо пользователь перейдет на страницу с комментариями – сервер отправит ему то же самое, что прислали на сервер, будь это текст с отзывом, картинка или взламывающий скрипт. Если мы добавим комментарий с текстом:
<script>alert('XSS');</script>
, то на странице будет отображаться пустой комментарий, при этом в момент загрузки страницы отработает скрипт, и у пользователя вылезет алерт «XSS». Если же в скрипте указано: «установить коннект с сервером ХХХ и загрузить туда куки», то это и произойдет – все, данные угнаны, при этом пользователь просто зашел на сайт.
Отраженные атаки за редким исключением не оставляют следов на сервере – в случае с отраженной XSS-атаки пользователь сам посылает вредоносный запрос со скриптом на сервер, а сервер отправляет этот запрос обратно, после чего уже скрипт выполняется и наносит ущерб. Для того, чтобы атака сработала, должны совпасть 2 условия:
Например, на сайте есть такая вот форма для поиска:
<form action="/search" method="get">
<label for="query">Поиск:</label>
<input type="text" id="query" name="query">
<button type="submit">Искать</button>
</form>
Когда данные передаются из формы на сервер – они вообще не проверяются, сервер просто дает то, что от него потребовали:
<?php
if (isset($_GET['query'])) {
$query = $_GET['query'];
echo "<p>Вы искали: " . $query . "</p>";
}
?>
В итоге если мы передадим ссылку с прописанным скриптом пользователю, он сам (через отраженный запрос от сервера) этот скрипт и запустит:
http://example.com/search?query=<script>alert('XSS');</script>
Эта атака, в отличие от двух предыдущих, вообще не касается сервера – она исполняется только на стороне клиента. «Ворота» для атаки здесь – тот факт, что с помощью JS можно изменять DOM (структуру документа); если фронтендер не позаботится о проверке входного значения в скрипте на клиентской стороне, атака будет возможна. Обычно атаку, как и в случае с отраженной, проводят через зараженную ссылку.
Например, на сайте есть страница поиска с таким кодом:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Поиск</title>
</head>
<body>
<form id="searchForm">
<label for="query">Поиск:</label>
<input type="text" id="query" name="query">
<button type="submit">Искать</button>
</form>
<div id="results"></div>
<script>
document.getElementById('searchForm').onsubmit = function(e) {
e.preventDefault();
var query = document.getElementById('query').value;
document.getElementById('results').innerHTML = "Вы искали: " + query;
};
</script>
</body>
</html>
На странице есть скрипт, который выводит запрос на странице результатов, скрипт манипулирует DOM. Вредоносная ссылка будет выглядеть так:
http://example.com/?query=<script>alert('XSS');</script>
Как только человек перейдет по ней, скрипт на клиентской стороне исполнится, и данные уйдут хакеру.
Дополнительно к этим трем часто выделяют 2 вида XSS по способу воздействия – активные и пассивные. Пассивная XSS-атака требует от пользователя определенных действий – перейти по ссылке, навести курсор на определенный элемент страницы и так далее. Активная атака ничего этого не требует – она срабатывает автоматически при соблюдении определенных условий (пользователь зашел на страницу, например).
Есть ряд «входных ворот», по которым вредоносные скрипты могут попасть к пользователю:
Тем, что персональные данные пользователей утекают в руки неизвестных лиц. Если эта атака направлена на похищение данных – в руках хакеров окажутся имена, фамилии, фото, данные банковских карт и любые другие данные, которые можно «угнать» с этой страницы (а в некоторых случаях – и с других страниц/сохраненных в браузере данных). Если же атака направлена на получение куки пользователя, то хакер на все время действия куки сможет получить доступ к личному кабинету пользователя – если, например, речь идет о сайте банка, то хакеры смогут переводить деньги на другие счета, если у пользователя не подключена двухфакторная аутентификация. А если хакер угонит куки администратора сайта, то получит вообще полный доступ ко всему, и тогда можно ожидать худшего.
В основном «ворота» для XSS-атак создаются по невнимательности – где-то разработчик забыл проверить ввод, где-то проверил его плохо, где-то «костылями» обошел встроенные во фреймворк ограничения и так далее. Соответственно, лучший способ защиты – это проверить вообще все, что связано с обработкой вводимой пользователями информацией:
Даже не пытайтесь написать собственный regex для санитизации инпута или написать скрипт, который будет отсекать «<», «>» или «<script></script>» – все это очень легко обходится за счет кодировки. Например, XSS-атака может выглядеть так:
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("атака")>
Обычно у людей, профессионально занимающихся взломом, есть инструменты для проверки «дыр» в самописных способах санитизации инпута – и поверьте, у вас этих дыр будет предостаточно.
В сети можно найти ряд бесплатных и платных инструментов для проверки сайта на уязвимости, этакие автоматизированные пентестеры. Если вы владеете небольшим сайтом, который хотите проверить – в теории такого инструмента может оказаться достаточно. Но эти инструменты обновляются довольно медленно, так что всегда будет шанс, что вы пропустите ошибку (или неправильно ее исправите) – живой пентестер будет лучше.