Защита сайта от CSRF (межсайтовая подделка запроса)
К списку статей
Существуют некоторые сходства между атаками с помощью межсайтового скриптинга (XSS) и атаками Cross-Site Request Forgeries (CSPF). Однако основной принцип у них все же разный и последние являются более серьезной угрозой, которую будет труднее отразить. При CSPF-атаке эксплуатируется доверие сетевого ресурса к пользователю.
CSPF-атаки характеризуются:
-Уязвимостями в привилегиях пользователя
Суть заключается в том, что зарегистрированным пользователям позволено осуществлять некоторые действия на сайте и если у них слишком высокие привилегии они автоматически подвергаются угрозе со стороны злоумышленников.
-Ложные пользователи
Хакер может взломать аккаунт доверенного пользователя и от его имени выполнять какие-то действия. Сайт при этом не будет ничего знать о подмене и по-прежнему будет ему доверять.
-Применение HTTP-запросов
Наиболее опасный и, пожалуй, самый распространенный признак CSRF-атаки. Пользователи будут выполнять запросы хакера, даже не догадываясь об этом.
По сути, ваш браузер это и есть HTTP-клиент, который взаимодействует с HTTP-сервером (веб-сервером). Рассмотрим пример отправки браузером запроса на сервер:
GET / HTTP/1.1
Host: example.org
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Здесь мы видим в первой строке: метод запроса и версию HTTP. Далее идут HTTP-заголовки и значения запроса после двоеточия.
Ответ сервера на такой запрос будет выглядеть так:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 57
< html>
< img src="http://example.org/image.png" />
< /html>
Здесь тег показывает браузеру, как корректно показать страницу. При этом браузер будет запрашивать URL, который прописан в атрибуте SRC и воспримет его как будто пользователь его сам задал. Браузер в таком случае не может знать, что от этого запроса ожидается.
А теперь представьте, насколько может быть опасной для пользователя ссылка такого вида:
http://stocks.example.org/buy.php?symbol=SCOX&quantity=1000
GET-метод в этом случае не отличается от простого запроса изображения. При этом, когда включена функция register_globals, для злоумышленника будет неважен даже метод.
Пользователь может купить себе 1000 акций компании SCOX, даже не зная об этом. Для этого ему нужно просто посетить страницу с тегом < img src="http://stocks.example.org/buy.php?symbol=SCOX&quantity=1000" />
В конечном итоге HTTP-запрос такого вида:
GET /buy.php?symbol=SCOX&quantity=1000 HTTP/1.1
Host: stocks.example.org
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Cookie: PHPSESSID=1234
Будет приравниваться к тому, что вы заполнили такую форму:
< p>Buy Stocks Instantly!< /p>
< form action="/buy.php">
< p>Symbol: < input type="text" name="symbol" />< /p>
< p>Quantity:< input type="text" name="quantity" />< /p>
< input type="submit" />
< /form>
Где Symbol = акции компании SCOX, а Quantity= количество равно 1000.
Защита от CSRF-атак
При составлении форм применяйте в их атрибутах POST-метод. Для простых форм, как в рассмотренном выше примере он вполне подойдет. Также не забывайте отключать register_globals. Вы должны понимать, что не следует давать пользователям слишком большие привилегии, ради удобства в использовании сайта. Если какое-то важное действие может быть совершено одним кликом - это будет целью №1 для CSRF-атаки.
Дабы усложнить жизнь хакеру используйте старую добрую капчу в отдельном окне идентификатора сессии. И если взять рассмотренный сайт объявлений из статьи о XSS-атаке и немного доработать ее, мы получим такой вид:
< ?php
$token = md5(time());
$fp = fopen('./tokens.txt', 'a');
fwrite($fp, "$token\n");
fclose($fp);
?>
< form method="POST">
< input type="hidden" name="token" value="" />
< input type="text" name="message">< br />
< input type="submit">
< /form>
< ?php
$tokens = file('./tokens.txt');
if (in_array($_POST['token'], $tokens))
{
if (isset($_POST['message']))
{
$message = htmlentities($_POST['message']);
$fp = fopen('./messages.txt', 'a');
fwrite($fp, "$message
");
fclose($fp);
}
}
readfile('./messages.txt');
?>
Однако хакер может заполучить токен, просто посетив эту страницу, он будет указан в HTML-коде. Так же можно заменить предсказуемую функцию "time()" допустим на iniqid() и rand(). Учитывая вышесказанное код должен выглядеть вот так:
< ?php
session_start();
if (isset($_POST['message']))
{
if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])
{
$message = htmlentities($_POST['message']);
$fp = fopen('./messages.txt', 'a');
fwrite($fp, "$message
");
fclose($fp);
}
}
$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;
?>
< form method="POST">
< input type="hidden" name="token" value="" />
< input type="text" name="message">< br />
< input type="submit">
< /form>