Сен 30 2008

Защита сайта от cross-site request forgery (CSRF)

Категория: Безопасностьgugglegum @ 00:54

Cross-site request forgery — это разновидность атаки на пользователя, при которой посещаемый веб-сайт выполняет несанкционированные действия, используя действующую авторизацию.

Предположим Вы зашли на сайт A, авторизовались там как пользователь (используя куки) и не завершая сессии зашли на сайт B, который использует CSRF-уязвимость сайта A и выполняет от Вашего имени какие-либо действия, например, постит сообщение, содержащее спам, поднимает репутацию какому-нибудь пользователю или что-нибудь еще в таком духе. А может быть еще и вот такой забавный сценарий, который некогда воплотился на сайте вконтакте.ру.

Прежде чем продолжить, хочу сразу сделать оговорку. CSRF — это в общем понимании не совсем уязвимость. Уязвимость — это когда при каких-то определенных условиях программа ведет себя не так, как задумывал ее разработчик. Но CSRF — это нормальное поведение всех сайтов в Интернете! Это можно сказать фича Интернета, которая иногда может быть использована во вред.

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

Иногда это бывает удобно. Например, мне как-то раз надоело вручную заполнять одни и те же заявки на неисправность у провайдера КабiNET и я на локалхосте создал форму один в один повторяющую поля и указал в качестве обработчика (action) обработчик на сайте провайдера. В результате, у меня была своя форма заявки, в которой уже были заведены основные шаблоны описаний неисправностей, нужно было лишь выбрать шаблон и нажать Отправить.

Это был пример мирного применения CSRF, но существуют естественно и враждебные, защиту от которых должен предусмотреть разработчик сайта. Ситуация осложняется тем, что враждебный сайт может использовать Javascript, чтобы произвести отправку формы (без всяких кликов, неважно GET или POST) в невидимом IFrame’е так, что пользователь даже не будет знать о том, что от его имени где-то что-то выполнилось. Страшно? :)

Как же защититься от враждебного применения CSRF?

В примитиве обработчик формы может просто проверять HTTP-заголовок Referer, где указывается с какого адреса пришел запрос и если адрес левый, то не обрабатывать запрос. Но этот заголовок не является обязательным и некоторые файрволлы (я точно знаю, что Agnitum Outpost Firewall) подменяют значение этого заголовка на название или адрес сайта файрволла. Поэтому полагаться на Referer все же не стоит.

Более грамотным решением является встраивание в форму дополнительного скрытого поля, содержащее какое-то значение, зависящее от текущей сессии пользователя. Самое простое и первое что приходит в голову — естественно идентификатор сессии. Враждебный сайт не сможет прочитать куку другого сайта (см. same origin policy) и соответственно не сможет подставить в скрытое поле правильное значение.

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

См. также описание CSRF на википедии.

8 отзывов на “Защита сайта от cross-site request forgery (CSRF)”

  1. maxd says:

    cookies приходят серверу и без данных формы.
    может при обработке данных формы просто проверять валидность сессии?

  2. gugglegum says:

    Нет, Макс! Сессия-то у тебя всегда будет валидной. Нам нужно удостовериться в том, что данные пришли не с сайта xxx.com, в котором форма отправлялась javascript’ом через невидимый iframe, а с нашего сайта. И дополнительный избыточный код в форме — это что-то вроде упрощенного варианта цифровой подписи самой формы, и этот код подтверждает: “эти данные отправлены с формы, расположенной на том же домене”. Потому что другой сервер на другом домене просто не смог бы получить ID сессии нашего сайта. Вот и все!

  3. maxd says:

    с тобой невозможно не согласиться - зришь в корень)))

  4. Lexx918 says:

    Есть редкое исключение, при котором браузер позволяет AJAX’у запросить удалённую страницу с “чужого” домена. В таком случае можно прочитать имя сессии в форме и не только! Подбробно уже написано добрыми людьми тут:
    http://www.inattack.ru/article/552.html

  5. gugglegum says:

    То, что IE позволяет отправлять запрос аяксом на чужой сайт — это его уязвимость, но я очень сомневаюсь, что через XMLHTTPRequest можно получить еще и cookie с другого домена. И google вроде как тоже говорит, что нельзя.

  6. Lexx918 says:

    Я имел в виду не куки, а значение скрытого поля, в котором предполагается хранить SID.
    Капча + SID + повторный ввод пароля спасут мир! ;-)

  7. B7W says:

    Есть непонятные моменты
    Ок, левый ресурс не может прочитать cookie. И post мы скрытно подписываем нашим cookie.csrf, и на сервере сверяем cookie.csrf == post.csrf.
    А как быть с get? Разве это безопасно в url отправлять? Вдруг HTTP Referer будет прочитан?
    И как часто надо генерировать cookie.csrf?

  8. const says:

    > А как быть с get?
    Не нужно через get получать параметры, которые могу управлять сайтом. GET только для получения страницы.
    > И как часто надо генерировать cookie.csrf?
    Желательно при каждой новой сессии с солью из чего-нибудь. А в идеале можно каждый новый раз, при открытии формы.

Напишите что Вы об этом думаете


*


*


rel=nofollow включен, спам не поднимет Page Rank Вашего сайта



Anti-Spam Image
Введите этот код, чтобы подтвердить, что вы человек
*

* — Обязательно для заполнения

Перейти на главную страницу