Welcome, anonymous (IP: 23.20.162.200). Sign in
Blog

Защита от CSRF и XSRF
Существуют разные виды атак на сайты, серьёзные и не очень. Например XSS и SQL Injection, защититься от которых легко, достаточно выпрямить руки.

CSRF и XSRF — атака напрямую связана с интернетом, а связана с тем, за что мы свободный интернет так обожаем: любой клиент может свободно зайти на сайт и свободно нагадить в базу данных. Спасает только то, что до сайта никому нет дела, но любой школьник мог бы написать однострочник на Bash, что потом всё это разгребать...

Говоря более конкретным языком, при отправке данных на сайт, которые будут записаны в базу данных или ещё как-то обработаны и выполнены, данные просто не проверяются, откуда и кем они были отправлены, и следовательно свободно обрабатываются, а это может быть любой паразитный трафик.

При отсутствии проверки на проведение CSRF и XSRF возможно выполнение любых несанкционированных действий методом GET-запросов. Атакующий может попросить вас открыть безобидную страницу, на которой содержится картинка, по адресу которой на самом деле будет выполнен подобный GET-запрос.
<img src="//localhost/admin/?do=FORMAT C:\" width="0" height="0" border="0">


Ваш браузер попытается загрузить картинку по указанному адресу, и это нормальное поведение браузера, но таким образом выполнив GET-запрос, который эм... форматирует ваш жёсткий диск. Грубо говоря, мягко выражаясь. GET-запрос может быть любым.

Казалось бы, но даже безобидным JavaScript'ом можно сканировать адреса и порты запрашивая документы вида http://192.168.0.1:8080/admin/logo.jpg, проверяя их доступность, и узнав например, по какому адресу находится домашний маршрутизатор, далее выполнять необходимые взломщику действия, и вы облегчаете задачу взломщику, если постоянно авторизованы в админ-панели маршрутизатора, — взломщик сразу попытается выполнить необходимый GET-запрос. Конечно, такой взлом реализуется не за одно посещение страницы и действительно должен быть направлен на вас, но скажем, день за днём посещая любимый веб-сайт, которому вы доверяете, откуда вы можете достоверно знать, что такие попытки взлома не совершаются?

На самом деле, масштаб трагедии эпичен.

К счастью, от классической CSRF-атаки защититься легко, достаточно проверять IP-адрес и/или HTTP-заголовок «Referer» клиента, с которого пришёл запрос на сайт. С этой целью генерируется так называемый CSRF-токен, что-то вроде пароля, но который неизвестен никому кроме сервера.
<form action="/admin/?CSRF=<php echo md5(serialize(array('IP' => $_SERVER['REMOTE_ADDR']))); >" method="POST">
</form>


Достаточно сгенерировать простой CSRF-токен, содержащий хэш-сумму проверяемой информации о клиенте. Суть в том, чтобы CSRF-токен нельзя было сгенерировать на стороне клиента, повторюсь, что это своего рода пароль, известный только серверу.

Разница несущественна, каким образом отправлять CSRF-токен, скрытым input-полем или параметром в URL.

Затем при проверке отправленных данных CSRF-токен восстанавливается и хэши сверяются.
$csrf = md5(serialize(array('IP' => $_SERVER['REMOTE_ADDR'])));
if (@$_GET['CSRF'] !== $csrf) {
	die('YOU SHALL NOT PASS');
}


При выполнении GET-запросов необходимо требовать подтверждения любых критичных действий со стороны пользователя, просто дополнительным нажатием на кнопку с последующей отправкой POST-запроса.

К сожалению, это не спасёт от направленной атаки, когда атакующему достаточно получить CSRF-токен и снова гадить на сайт уже с использованием CSRF-токена. Добавление каких-либо ещё данных, усложняющих процесс, не поможет.

В качестве примера если только... Можно добавить скрытое input-поле с датой и временем, и тогда при каждом новом заходе будет сгенерирован новый CSRF-токен с учетом даты и времени, но тогда атакующий перед отправкой следующей порции данных будет просто заходить на сайт, раз за разом получать новый CSRF-токен и продолжит своё недоброе дело. В принципе, использование каких-либо данных кроме IP-адреса клиента и HTTP-заголовка «Referer» излишне и никак не поможет.

Самый верный способ — генерировать CSRF-токен с использованием уникальных данных о клиенте, которые известны только серверу, не забыв «посолить» их.

И, конечно же, следует добавить капчу. Рекомендую так же запоминать капчу в CSRF-токене, это избавит от необходимости хранить капчу в какой-то отдельной базе данных.

Следующий SQL-запрос поможет сделать функцию капчи более дружелюбной.
$count = $dbh->query('
	SELECT COUNT(*)
	FROM comments
	WHERE ip = "'.$_SERVER['REMOTE_ADDR'].'" AND date > datetime("now", "-5 minutes")
')->fetchArray()['COUNT(*)'];


Не следует принимать всех посетителей сайта за спам-ботов, отталкивая их необходимостью лишний раз вводить капчу.

Представленный SQL-запрос вернёт количество комментариев, которые оставил клиент за последние 5 минут. Затем можно добавить проверку, что если клиент оставил более 2х комментариев за последние 5 минут, то для добавления ещё одного комментария ему уже необходимо ввести капчу.

Данный метод не отпугнёт рядового пользователя, решившего написать комментарий, но и не пропустит откровенный спам.
Author: Spoofing , @ , WWW
Published on: 2015-05-23 15:32:02
Views: 1413
Comments: 2
Comments
Write a Comment:
 (Your comment will appear after it is approved)
 (Not over than 9000 characters)

anonymous
2015-05-27 13:33:22
А мне больше нравится способ более классический с csrf токеном:
в cookie сохраняется рандомное значение, и клиент при запросах передает значение из cookie.
На сервере идет проверка при каждом запросе на совпадение токена из cookie и токена из get параметра (либо в input'е каком-нибудь передано). Таким образом злоумышленник не сможет никак узнать значение из cookie клиента, для того, чтобы реализовать атаку, описанную вначале (конкретно пример с переходом по ссылке //localhost/admin/?do=FORMAT C:\".
Copyright © Spoofing. All rights reserved.