[Из песочницы] Методы защиты от CSRF-атаки
Что такое CSRF атака?
Ознакомиться с самой идеей атаки CSRF можно на классических ресурсах:
- OWASP
- Acunetix
- Отличный ответ на SO
Выдержка из ответа на SO:
Причина CSRF кроется в том, что браузеры не понимают, как различить, было ли действие явно совершено пользователем (как, скажем, нажатие кнопки на форме или переход по ссылке) или пользователь неумышленно выполнил это действие (например, при посещении bad.com
, ресурсом был отправлен запрос на good.com/some_action
, в то время как пользователь уже был залогинен на good.com
).
Как от нее защититься?
Эффективным и общепринятым на сегодня способом защиты от CSRF-Атаки является токен. Под токеном имеется в виду случайный набор байт, который сервер передает клиенту, а клиент возвращает серверу.
Защита сводится к проверке токена, который сгенерировал сервер, и токена, который прислал пользователь.
А что, собственно, защищать?
Если вы пишете свой Web-Сервис в соотвествии со стандартом RFC7231, то методы GET
, HEAD
, OPTIONS
и TRACE
являются безопасными: они предназначены только для получения информации и не должны изменять состояние сервера.
Таким образом, защищать необходимо небезопасные методы, к которым относятся: POST
, PUT
, DELETE
, PATCH
.
На Habrahabr опубликована статья от Yandex, в которой описано, почему писать свои сервисы нужно, руководствуясь стандартом.
Требования к токену:
- Уникальный токен для каждой операции
- Действует единожды
- Имеет размер, устойчивый к подбору
- Сгенерирован криптографически стойким генератором псевдослучайных чисел
- Имеет ограниченное время жизни
На первом MeetUp’е PDUG Тимур Юнусов (руководитель отдела безопасности банковских
систем Positive Technologies) рассказывал, почему именно такие требования предъявляются к CSRF-Токену и чем грозит пренебрежение ими.
Требования к Web-Сервису и окружению:
Отсутствие XSS уязвимостей
Внедренный злоумышленником скрипт имеет возможность отправлять запрос к серверу от имени пользователя и читать его без каких-либо препятствий.
Таким образом, XSS уязвимости могут быть использованы для получения текущего токена.
Отсутствие malware на машине клиента
Если злоумышленник имеет возможность запускать софт на машине клиента, то он может получить любые данные, имеющиеся в браузере.
Методы защиты
Существует 3 метода использования токенов для защиты web-сервисов от CSRF атак:
- Synchronizer Tokens (Statefull)
- Double Submit Cookie (Stateless)
- Encrypted Token (Stateless)
Synchronizer Tokens
Простой подход, использующийся повсеместно. Требует хранения токена на стороне сервера.
Суть:
При старте сессии на стороне сервера генерируется токен.
Токен кладется в хранилище данных сессии (т.е. сохраняется на стороне сервера для последующей проверки)
В ответ на запрос (который стартовал сессию) клиенту возвращается токен.
Если рендеринг происходит на сервере, то токен может возвращаться внутри HTML, как, например, одно из полей формы, или внутри
тега.
В случае, если ответ возвращается для JS приложения, токен можно передавать в header (часто для этого используют
X-CSRF-Token
)При последующих запросах клиент обязан передать токен серверу для проверки.
При рендере контента сервером токен принято возвращать внутри POST данных формы.
JS приложения обычно присылают XHR запросы с header (
X-CSRF-Token
), содержащим токен.При получения запроса небезопасным методом (
POST
,PUT
,DELETE
,PATCH
) сервер обязан проверить на идентичность токен из данных сессии и токен, который прислал клиент.Если оба токена совпадают, то запрос не подвергся CSRF-Атаке, в ином случае — логируем событие и отклоняем запрос.
На выходе имеем:
Защита от CSRF на хорошем уровне
Токен обновляется только при пересоздании сессии, а это происходит, когда сессия истекает
Во время жизни одной сессии все действия будут проверяться по одному токену.
Если произойдет утечка токена, то злоумышленник сможет выполнить CSRF-Атаку на любой запрос и в течение долгого срока. А это не есть хорошо.
Бесплатная поддержка multi-tab в браузере.
Токен не инвалидируется после выполнения запроса, что позволяет разработчику не заботиться о синхронизации токена в разных табах браузера, так как токен всегда один.
Double Submit Cookie
Этот подход не требует хранения данных на стороне сервера, а значит, является Stateless. Используется, если вы хотите уметь быстро и качественно масштабировать свой Web-сервис горизонтально.
Идея в том, чтобы отдать токен клиенту двумя методами: в куках и в одном из параметров ответа (header или внутри HTML).
Суть:
При запросе от клиента на стороне сервера генерируется токен. В ответе токен возвращается в cookie (например,
X-CSRF-Token
) и в одном из параметров ответа (в header или внутри HTML).В последующих запросах клиент обязан предоставлять оба полученных ранее токена. Один как cookie, другой либо как header, либо внутри POST данных формы.
При получении запроса небезопасным методом (
POST
,PUT
,DELETE
,PATCH
) сервер обязан проверить на идентичность токен из cookie и токен, который явно прислал клиент.Если оба токена совпадают, то запрос не подвергся CSRF-Атаке, в ином случае — логируем событие и отклоняем запрос.
На выходе имеем:
Stateless CSRF защиту.
Необходимо учитывать, что поддомены могут читать cookie основного домена, если явно это не запрещать (т.е. если cookie установлена на
.site.ru
, то её могут прочитать какa.site.ru
, так иb.site.ru
).Таким образом, если ваш сервис доступен на домене 3-го уровня, а злоумышленник имеет возможность зарегистрировать свой ресурс на вашем домене 2-го уровня, то устанавливайте cookie на свой домен явно.
- Нюансы зависят от реализации
Encrypted Token
Так же как и Double Submit, является Stateless подходом. Основная — если вы зашифруете надежным алгоритмом какие-то данные и передадите их клиенту, то клиент не сможет их подделать, не зная ключа. Этот подход не требует использования cookie. Токен передаётся клиенту только в параметрах ответа.
В данном подходе токеном являются факты, зашифрованные ключом. Минимально необходимые факты — это идентификатор пользователя и timestamp времени генерации токена. Ключ не должен быть известен клиенту.
Суть:
При запросе от клиента на стороне сервера генерируется токен.
Генерация токена состоит в зашифровке фактов, необходимых для валидации токена в дальнейшем.
Минимально необходимые факты — это идентификатор пользователя и timestamp. В ответе токен возвращается в одном из параметров ответа (В header или внутри HTML).
В последующих запросах клиент обязан предоставлять полученный ранее токен.
При получения запроса небезопасным методом (
POST
,PUT
,DELETE
,PATCH
) сервер обязан валидировать токен, полученный от клиента.Валидация токена заключается в его расшифровке и сравнения фактов, полученных после расшифровки, с реальными. (Проверка timestamp необходима для ограничения времени жизни токена)
Если расшифровать не удалось либо факты не совпадают, считается, что запрос подвергся CSRF-Атаке.
На выходе имеем:
Stateless CSRF защиту
Нет необходимости хранить данные в cookie
- Нет нюансов с поддоменами.
О реализации
Давайте генерировать новый токен на каждый запрос, не важно, каким HTTP-методом и с какой целью этот запрос сделан.
Таким образом мы получаем токен, который меняется постоянно.
Конечно, возникает вопрос организации multi-tab работы.
Синхронизация токенов между табами может быть реализована с использованием localStorage и его StorageEvent
Ограничиваем время жизни cookie, которое содержит токен, разумным значением. Например 30 минут.
Делаем cookie недоступной из JS (ставим HTTPOnly=true)
Используем TLS для предотвращения MITM
При этом отправляем cookie только по HTTPS (ставим Secure=true)
Размер токена не менее 32 байт.
Генерируем токен криптографически стойким генератором псевдослучайных чисел.
Для этого можно использовать системные функции:
Linux => getrandom(2) если возможно, /dev/urandom иначе OpenBSD => getentropy(2) На других Unix-like системах => /dev/urandom Windows => CryptGenRandom API
Что еще нужно знать?
Токены — обязательная защита от CSRF.
Проверяйте, но не полагайтесь только на
X-Requested-With: XMLHttpRequest
Проверяйте, но не полагайтесь только на заголовки:
Host
,Origin
,Referer
Не передавайте токены в URL
- Защищайте все запросы.
Same Site
Сейчас идет работа над спецификацией атрибута «Same-Site» у cookies (последняя версия на момент написания статьи).
Такой атрибут даст возможность разработчикам явно указывать, что cookie не нужно передавать, если запрос идет с сайта, отличного от того, на котором cookie была установлена. А, значит, у нас появится возможность защищать ресурсы от CSRF без использования дополнительных инструментов.
Браузер Chrome уже сейчас поддерживает эту возможность.
Чуть больше информации о том, как и почему доступно на Stack Exchange.
Комментарии (4)
29 декабря 2016 в 15:47
0↑
↓
Шифрация/дешифрация ресурсоемкие операции. Гораздо проще использовать хэширование. В простейшем случае у нас будет код наподобие:$time = time(); $token = md5(SECRET_KEY . $userData . $time) . ':' . $time;
Как проверить такой токен, думаю, понятно.29 декабря 2016 в 15:50
0↑
↓
Боюсь, так можно достаточно быстро подобрать SECRET_KEY29 декабря 2016 в 15:53 (комментарий был изменён)
0↑
↓
Зря боитесь:$time = time(); $secretKey = "33Sht?U
@xy8sah3uwA?T( 29 декабря 2016 в 15:59
0↑
↓
(да, мой числовой id 260599:)
Хорошо, я забыл, что SECRET_KEY не пароль и может быть длинным)