CSRF-атаки: хакер действует от имени вашего клиента
АйТи Фреш
Информационная безопасность

Как сайт без защиты от CSRF превращает вход клиента в оружие против него

Автор: , директор ООО «АйТи-Фреш» · · ~11 мин чтения
Нить кукловода направляет руку клиента при подписи
CSRF: действие совершается от имени клиента, а он об этом даже не знает

CSRF-атака — это когда чужой сайт нажимает кнопки в вашем личном кабинете руками вашего же клиента. CSRF расшифровывается как Cross-Site Request Forgery, межсайтовая подделка запроса. Клиент ничего специально не кликает и ничего не скачивает. Достаточно, чтобы он был залогинен у вас в одной вкладке и открыл безобидную на вид страницу в другой. Всё. Телефон в профиле сменён, адрес доставки переписан, новый администратор добавлен.

CSRF — идеальное преступление: жертва сама вошла, сама открыла страницу и даже не заметила, что что-то подписала.

Как это работает

Механика обидно простая. Браузер автоматически прикладывает cookie сессии к каждому запросу на домен, где эта сессия открыта. Неважно, с какой страницы запрос отправлен — с вашей или с чужой.

Клиент залогинен в кабинете. В соседней вкладке открывает страницу со спрятанной формой, картинкой с хитрым адресом или скриптом. Браузер послушно отправляет запрос на ваш сайт — с теми же cookie. Сервер видит действующую сессию и думает: «А, это наш клиент». Формально он прав. Сессия действительно клиента. Только клиент об этом запросе не подозревает.

CSRF и XSS — не одно и то же

Аббревиатуры путают постоянно. Разница принципиальная.

XSS — это чужой код, внедрённый в страницу вашего сайта. Атака идёт изнутри вашего домена.

CSRF на ваш сайт ничего не внедряет. Вредоносная страница живёт на совершенно другом домене и просто отправляет к вам запрос от имени авторизованного пользователя. Эксплуатируется не дыра в вашем коде, а особенность работы cookie и HTTP.

Отсюда вывод: защита от XSS и SQL-инъекций от CSRF не спасает. Это отдельный слой обороны. Отдельный пункт проверки.

Что можно натворить чужими руками

Любое действие, которое у вас выполняется без дополнительной проверки на сервере:

Жертва ничего не видит. Страница открылась, страница закрылась. Предупреждений нет. Действие выполнено.

Пароль атакующему не нужен

Вот что особенно неприятно. CSRF не требует ни подбирать пароль, ни перехватывать трафик, ни ломать шифрование. Атака идёт после честного входа пользователя.

Длинные пароли, сложные правила, двухфакторная аутентификация при входе — всё это прекрасно и всё это бесполезно против CSRF. Пользователь уже вошёл. Сессия открыта. Дальше работает браузер.

Поэтому про CSRF и забывают. «У нас вход защищён — значит, всё защищено». Это как поставить бронированную дверь и оставить на ней записку «ключ под ковриком» для всех, кто уже внутри.

Сценарий: кабинет интернет-магазина

Магазин с личным кабинетом. Можно сохранить карту для быстрой оплаты и привязать телефон. Форма смены телефона принимает новое значение и сохраняет его, не проверяя, пришёл ли запрос со страницы самого магазина.

Клиент залогинен. Переходит по ссылке из письма на сторонний ресурс. Там тихо срабатывает запрос на смену телефона в его профиле. Атакующий запускает восстановление пароля — код приходит на подменённый номер. Полный доступ к кабинету со всеми сохранёнными данными.

Клиент узнаёт об этом, когда не может войти. Магазин — когда клиент пишет гневный отзыв. Подрядчик не узнаёт никогда, потому что гарантия давно закончилась. А отвечать перед клиентом и, возможно, перед регулятором за утёкшие персональные данные придётся владельцу магазина. Не разработчику.

Скрытый рычаг уводит посылку на чужой адрес
Подделанный запрос меняет адрес доставки в кабинете покупателя

Сценарий: внутренняя админка

Вторая картина. Сотрудник весь день держит открытой админ-панель сайта или CRM в браузере. Формы панели не защищены от CSRF.

Сотрудник открывает любую постороннюю страницу — новость, «прайс от поставщика», что угодно. Через его сессию добавляется новый администратор, меняются реквизиты получателя платежей, выгружаются данные.

Внутренние панели часто остаются без защиты дольше, чем публичный сайт. «Туда же чужие не ходят». Для CSRF это не аргумент. Важна не публичность панели, а открытая сессия в браузере сотрудника. А она открыта всегда.

Почему разработка забывает про CSRF

Потому что CSRF не ломает ничего видимого. Форма отправляется, данные сохраняются, менеджер доволен. Ошибка проявляется только при специальной проверке: примет ли сервер тот же запрос с чужого домена.

Заказчик такую проверку делать не умеет. Дешёвый подрядчик её не предлагает. В договоре её нет. Получается тёмная зона, о которой ни одна сторона не спросила другую.

Теперь резко. Сдать сайт с личным кабинетом, платёжными формами и админкой без защиты от подделки запросов — это не «упустили мелочь». Это сдать банковскую ячейку с замком, который открывается любым письмом со ссылкой. Защита от CSRF во многих фреймворках включена по умолчанию. Чтобы её не было, разработчику надо либо писать всё вручную и забыть, либо сознательно отключить её, потому что «мешала». Оба варианта говорят о подрядчике больше, чем его портфолио.

Работающая форма и безопасная форма — это два разных акта приёмки.

Защита №1: CSRF-токен

Главный и самый надёжный способ. Сервер генерирует уникальный токен, привязанный к сессии или форме, и вставляет его в страницу. При отправке формы токен возвращается, сервер сверяет его перед выполнением действия.

У чужого сайта токена нет и быть не может: он не видит содержимое вашей страницы. Значит, подделать запрос с правильным токеном не получится, даже если браузер жертвы честно приложит cookie.

Одна оговорка, на которой горят. Токен должен проверяться. Видел системы, где токен генерировался, красиво выводился в форму и… нигде не проверялся. Декорация. Как камера наблюдения без кассеты.

Привратник сверяет печать на конверте с перстнем
CSRF-токен: сервер принимает только запросы с правильной печатью

Защита №2: SameSite у cookie

Атрибут SameSite говорит браузеру, отправлять ли cookie с запросами, пришедшими с другого сайта.

Хорошая новость: Chrome начиная с 80-й версии применяет Lax по умолчанию, если атрибут не задан явно. Плохая: «по умолчанию в одном браузере» — не стратегия безопасности. Задавайте атрибут явно и не забывайте про токены.

Дополнительные барьеры

Ни одна мера в одиночку не закрывает вопрос. Основа — токен плюс правильные cookie. Остальное — запас прочности.

«В рейтингах уязвимостей CSRF больше нет»

Встречаю такой аргумент. В актуальном OWASP Top 10 действительно на первом месте Broken Access Control, нарушение контроля доступа, а CSRF отдельной топовой категорией не выделяется. Отчасти потому, что современные фреймворки — Django, Laravel и другие — включают защиту от CSRF из коробки.

Но защита из коробки работает, пока коробку не вскрыли. Самописный код, старые CMS, формы в обход фреймворка, «временные» обработчики, которые живут пятый год, — всё это уязвимо так же, как десять лет назад. Отсутствие пункта в рейтинге не означает отсутствие дыры на вашем сайте.

Рейтинг уязвимостей — это статистика по всем. Взлом — это всегда про конкретно вас.

Мобильные приложения и API

Если API авторизует запросы токеном в заголовке, а не cookie браузера, классический CSRF там не работает: браузер сам такой заголовок не приложит. Но почти у любого API рано или поздно появляется веб-кабинет с авторизацией через cookie. И уязвимость возвращается — как раз в этой веб-части.

Одностраничные приложения на React или Vue не защищены автоматически. Авторизация через cookie — значит, CSRF актуален так же, как для классического сайта.

Быстрая самопроверка без хакерских навыков

Полноценный аудит — работа специалиста. Но первые признаки можно увидеть самому за четверть часа, ничего не ломая.

Шаг первый. Откройте в браузере форму смены профиля или пароля и посмотрите код страницы. Ищите скрытое поле со словом token, csrf или похожим. Нет ничего похожего ни в одной форме? Повод для серьёзного разговора с подрядчиком.

Шаг второй. В инструментах разработчика браузера откройте список cookie вашего сайта. Найдите cookie сессии и посмотрите колонку SameSite. Пусто — значит, атрибут не задан, и вы надеетесь на настройки браузера по умолчанию.

Шаг третий. Посмотрите на ссылки в админке. Если удаление записи или смена статуса выполняется переходом по адресу вида «…/delete?id=15», действие идёт через GET. Такую ссылку можно подсунуть в картинку на любой странице.

Три шага не заменяют проверку. Зато отлично отрезвляют перед звонком подрядчику.

Что делаем мы и чем это отличается

Типичный подрядчик на сдаче проверяет, что форма «работает»: отправилась, сохранилась, письмо пришло. Мы на обслуживании проверяем другое: работает ли форма только тогда, когда запрос пришёл с вашего сайта.

Обслуживание сайта и сервера у нас — от 7 000 ₽ в месяц, проверка защиты от CSRF входит в него. Нужен разовый аудит без обслуживания — посчитаем объём под ваш сайт.

Чек-лист для заказчика

Проверьте до того, как проверят за вас

Не уверены, что формы вашего сайта отличают клиента от чужой страницы, открытой в соседней вкладке? Напишите нам в АйТи Фреш. Посмотрим код, настройки cookie и админки — и закроем дыру до того, как ею воспользуются.

Частые вопросы

HTTPS защищает от CSRF?

Нет. HTTPS шифрует канал между браузером и сервером, но не проверяет, с какого сайта запущен запрос. Шифрование и защита от CSRF — независимые вещи.

CSRF опасен только для форм с паролями и платежами?

Нет. Опасно любое действие, меняющее состояние: настройки профиля, права доступа, подписки, заявки. Чем ценнее действие, тем больнее последствия, но защищать нужно все такие формы.

У нас SPA на React. Нам CSRF не грозит?

Грозит, если авторизация идёт через cookie. Если фронтенд сам кладёт токен в заголовок запроса, классический вектор усложняется, но тогда нужно проверить, как сервер валидирует этот токен.

Хватит одного токена на весь сайт?

Токен должен быть привязан к сессии и проверяться при каждом изменяющем действии. Решает не наличие токена, а проверка. Непроверяемый токен защищает ровно ни от чего.

Как быстро закрыть CSRF на живом сайте?

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

Сколько стоит проверка у вас?

Входит в обслуживание сайта и сервера — от 7 000 ₽ в месяц. Разовый аудит считаем отдельно под конкретный сайт.

Защита от CSRF замедлит сайт?

Нет. Генерация и проверка токена — микроскопическая нагрузка по сравнению с обычной обработкой формы. Пользователь разницы не заметит. Заметит только атакующий.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

© ООО «АйТи-Фреш» · Москва · Все статьи