Pull to refresh

Comments 93

А еще можно просто не использовать GET и POST запросы для операций, связанных с изменением данных.
Как будто другие виды запросов более защищены…
Так же, как и Referer.
Старая версия флэша. Или ошибка в браузере, который еще не создан. Или ошибка в версии флэша, которую еще не выпустили.
А что вы предложите использовать?
Автор имел в виду, что, к примеру, PUT запрос можно послать ajax'ом, но невозможно отправкой формы (CSRF-атакой). В принципе прикольная идея. Не уверен в практичности, но прикольная.
Сама идей, на мой взгляд, довольно интересная. Но у нее есть два недостатка:

1) Повышается сложность разработки: вместо того, чтобы добавить новое скрытое поле в форму, генерировать токен и проверять его на стороне сервера (реализация всех этих действий требует не очень много кода и совсем небольшое вмешательство в уже существующий), вам необходимо написать посредника для получения данных из формы (чем сложнее форма, тем больше кода и потенциальных ошибок) и отправки их на сервер. На серверной стороне вам надо будет переписать код, отвечающий за прием данных и их валидацию. И необходимо убедиться, что не происходит обработки POST/GET запросов, изменяющих данные пользователя. Я наблюдал много схожих ошибок у тех, кто активно использует AJAX на сайте: на сервере нет проверки, пришли ли данные через AJAX или обычную отправку формы. Это просто рай для любителя CSRF.

2) Вам надо следить за возможными обновлениями браузеров: я видел некоторое количество предложений разрешить использовать тот же PUT в обычных формах. Если же вы правильно реализовали механизм токенов, то вам не надо этого боятся. Проверку токена на серверной стороне не сможет отменить ни один браузер.
Да в общем-то наоборот, это позволяет надежно исключить практически все указанные ошибки — тип запроса или наличие заголовка можно проверять не в коде скрипта, а на балансере, реверс прокси или веб-сервере, что позволяет исключить человеческий фактор и ошибки программистов. К тому же, поскольку доступ в таком случае управляется CORS, при необходимости легко разрешить некоторым доверенным сайтам делать межсайтовые запросы.

Вторая проблема, если честно, кажется надуманной, но легко устраняется проверкой X-Requested-With.
Я не спорю, что эти недостатки можно обойти. Но эти методы я бы отнес к разряду лучших практик для достаточно крупных сайтов. Например, для многих сайтов, расположенных на shared-хостинге, использование балансера, реверс прокси или фильтрации на уровне сервера весьма затруднительно. А добавление токенов значительно проще.

Или пример на основе реального проекта вашей компании. Речь идет о LOTRO. Когда на сайте этой игры всплыли возможности для CSRF, разработчики сначала добавили проверку на Referer, так они поступили с каждой новой CSRF, которая была найдена. Потом им сообщили о возможности использования одного токена для разных пользователей (что-то вроде токена применялось в некоторых формах, алгоритм выбора которых я так и не понял). Через некоторое время они применили несколько попыток сделать защиту от кликджекинга. А уже после этого они, наконец-то, добавили токены во все критичные запросы на сайте. Как вы понимаете, речь про замену метода запросов на отличный от GET/POST даже не шла.
Редиректить на ошибку при неправильном типе запроса/отсутствующем заголовке можно средствами стандартного mod_rewrite.

Ограничения есть, но они другие: этот подход годится только для сайтов, где все запросы аяксовые.
Изначальный вопрос в этой ветке — использование не POST/GET для изменения пользовательских данных.
А еще можно просто не использовать GET и POST запросы для операций, связанных с изменением данных.

По вашим же словам, данный способ подходит только для сайтов, использующих AJAX. Для других сайтов надо либо заменять все запросы на AJAX (это весьма трудоемко), либо применять другие методы защиты. Использование методов, отличных от POST/GET не является универсальным решением. Так что вопрос создания надежной системы токенов остается важным.
UFO just landed and posted this here
В каком случае не спасет проверка на аякс?
Злоумышленник же не может отправить CSRF запрос AJAXом с другого домена (если не стоят разрешающие заголовки на сервере). Но может отправить форму.
UFO just landed and posted this here
>> Повышается сложность разработки

Не вижу принципиальной разницы между обработкой POST или PUT, PATCH, DELETE, whatever (если знаешь, что такое php://input).

>> Если же вы правильно реализовали механизм токенов, то вам не надо этого боятся

В том-то и дело, написание хорошего генератора-обработчика токенов — нетривиальная задача. Может показаться, что реализовали все правильно, а на самом деле — дуршлаг.

>> Вам надо следить за возможными обновлениями браузеров: я видел некоторое количество предложений разрешить использовать тот же PUT в обычных формах

Это да, я согласен. Но не думаю, что на это пойдут.
Вообще я не утверждаю, что это супер-пупер-серебряная пуля, но как одну из возможных стратегий защит от CSRF рассматривать стоит.
на сервере нет проверки, пришли ли данные через AJAX или обычную отправку формы
Вы имеете ввиду http заголовок X-Requested-With? Но ведь его можно подделать на клиенте. Или есть какие то другие способы проверить это?
Не, так не надежно. Если не хотите, что бы у вас украли что-то из интернета, не передавайте эти данные в интернет вот и всего делов-то ))
Можно и POST с GET, но ставить дополнительный заголовок / проверять его наличие.
Кто помешает злоумышленнику установить дополнительный заголовок при осуществлении атаки? Полагаю, что имеет смысл, только если в заголовке будет передаваться все тот же секретный ключ
CORS помешает, для заголовков отличных от Accept и Accept-Language делается preflight-request.
Кроме того, он может быть подделан с использованием старых версий Flash, что подставляет под удар тех, кто очень долго ничего не обновлял на своем компьютере.
ОЧЕНЬ старой.
Объясните пожалуйста, зачем нужна проверка этого CSRF-токена. Чем опасно обращение с другого сайта? Допустим у меня есть некий API, через который пользователь взаимодействует с удаленным сервером. Взаимодействовать он может как через веб интерфейс, так и через приложение. Допустим, что если в первом случае проверка CSRF оправдана, то во втором зачем она нужна, если есть сессия например? И нужна ли вообще? Или CSRF используется только для доступа с браузера? А зачем, если, например, можно просто сделать апи и неважно, какой там клиент на другой стороне: браузер или десктоп приложение. Надеюсь, ясно выразился и удалось разобрать, какая информация меня интересует )
В статье идет речь исключительно о формах. Если у вас REST-API, который сам по себе защищен, а сайт только клиент, то беспокоится, в общем не о чем. Другое дело, что когда клиентом является сайт, то формы на нём так или иначе будут, и опять же из них можно перехватить и использовать во зло.
Надеюсь, более-менее объяснил :)
Предположим, что пользователь на Вашем на сайте может выполнять какие-то действия только авторизовавшись. Тогда, если он уже авторизован (сессия запущена), при попадании на сайт злоумышленника, с этого сайта от его имени может быть отправлен POST-запрос на изменение каких-либо данных, или GET-запрос на получение приватной/конфиденциальной информации.
Допустим, есть некоторый сайт, где в админке есть функция удаления этого самого сайта (ну, чтобы страшнее было). Допустим, эта функция доступна по GET-запросу на URL example.com/admin/deleteall?b=yes

Злоумышленник знает о существовании этой ссылки. Он вводит ее в любой сокращатель ссылок, получая безобидную ссылку goo.gl/cC68N6 Теперь он отправляет эту ссылку по скайпу админу сайта, админа редиректит на example.com/admin/deleteall?b=yes — и, если админ был залогинен в админке, весь сайт удаляется. Атака удалась.

Если используется POST — то атака становится сложнее, но не перестает быть возможной от этого: form.submit() еще никто не отменял.

Необходимым условием является возможность для злоумышленника составить правильный запрос, чтобы потом передать его для выполнения жертве. Сама передача, как видно, осуществляется очень просто. CSRF-токен является элементом запроса, который злоумышленник не способен предсказать. Если обновить админку сайта, чтобы ссылка на удаления была вида example.com/admin/deleteall?b=yes&token=/token/, где /token/ — уникальный секрет, либо одноразовый, либо привязанный к сессии пользователя — то злоумышленник не сможет угадать его, составить запрос и в итоге не сможет провести атаку.
По поводу последнего пункта весьма спорно. В качестве примера возьму свою любимую ссылку /logout/ на «выход» с ресурса. Рассмотрим пример классической атаки: модератор форума не может забанить пользователя за сообщение в ветке — каждый раз он получает ошибку и потом теряет сессию. Хитрый пользователь просто разместил в сообщении bb-код [img]http://example.com/logout[/img].
А где у хитрого пользователя POST?
У нас не особо хитрый разработчик который сделал logout через GET в этом примере.
К данному примеру у меня есть два замечания:

1) Как и говорили выше, логичнее делать такие запросы через POST, а пользователям не давать возможность вставлять необработанный HTML в сообщения. И, конечно, стоит использовать токен.

2) С формальной точки зрения, данный пример опровергает бесспорность первого пункта. Если же подходить менее формально, то ситуация несколько другая: хотя токен и не был одноразовым, при выходе с сайта он стал таковым: когда пользователь покинул сайт, его старый токен должен стать недействительным, а новый он получит при следующем входе на сайт. В данном конкретном случае раскрытие такого токена не позволяет провести атаку с его использованием.
Я только одного не понимаю, народ пишет про все эти методы защиты, referer, другой тип запроса, токен и прочее…

какой в этом вообще смысл, последнее время когда совершается подобная атака, все запросы делают вообще сервером, соответственно проставляют любые заголовки в запрос, распарсивают страницу и находят любой токен с нее…

по сути взломщик полностью эмулирует деятельность пользователя…

как раз тема с капчей более менее рабочая, хоть и требует доп действий от пользователя. Еще возможно собирать метрики пользователей и как то пытаться проверять поведение: «робот не робот»
Смотрите, тут дело несколько в другом. CSRF атака нужна не для того, чтобы эмулировать действия пользователя и оставлять всякие рекламные сообщения на сайте. CSRF предназначена для того, чтобы выполнить от имени пользователя (злоумышленник не имеет доступа к его аккаунту) запрос на сайт (этот запрос пользователь тоже не хочет выполнять). Подобные методы не защитят вас от спамеров. Но помогут защитить ваших пользователей от CSRF, чтобы те не лишились своих аккаунтов.
это конечно ясно, но сейчас очень много смежных атак при которых делается фишинг страница, пользователь самостоятельно вводит в ней данные, а уже далее их уже с ними эксплуатируют сервис, в таком случае как раз капча очень не плоха
CSRF-атак становится все меньше исключительно потому что от них учатся защищаться. Убери защиту — и это быстро обнаружат.

PS капчу можно показать на той самой фишинговой странице, чтобы пользователь не только подарил свои данные злоумышленнику — но и капчу заодно разгадал.
UFO just landed and posted this here
Десятый пункт неоднозначен: точно так же можно предположить что пользователь сохранит веб страницу и отправит ее кому-то. В этом случае так же будут скомпрометированы все токены в формах.
>> 8) Предсказумые токены.

А зачем генерить токен на основе каких-то данных? Почему нельзя взять просто рандомную строку?
Рэндомные токены будут теряться при прерывании сессии (пишете вы длинную статью на хабр, а за это время сессия истекла и токен больше недействителен). Поэтому и возникает соблазн сделать токен таким, чтобы пользователь мог его использовать даже в случае прервавшейся сессии.
При прерывании сессии пользователь не сможет сохранить статью даже с валидным токеном…
Если в куках имеются данные для автологина, то без проблем сможет. Более того, даже не заметит что она прерывалась.
В алгоритме генерации токена на основе пользовательских данных должна быть некая условно случайная величина, чтобы токен все время новый получался. Например, метка времени. Чтобы проверить валидность токена, нужно где-то хранить эту метку времени. Если ее хранить в сессии, то в вашем примере токен протухнет тоже. Если хранить в БД, тогда мы и рандомные токены можем точно так же хранить в БД.
Я не говорил что так надо делать. Я отвечал на вопрос «какие проблемы решает данный подход». Читайте внимательнее.
Вы написали

>> Рэндомные токены будут теряться при прерывании сессии

Я вам доказал, что при прерывании сессии рандомные токены будут протухать не более, чем обычные. Поэтому изначальный вопрос:

>> А зачем генерить токен на основе каких-то данных? Почему нельзя взять просто рандомную строку?

а точнее «Чем рандомные токены хуже нерандомных» остается открытым.
Нерэндомные токены генерируются на основе нерэндомных данных, как следует из названия. Раз данные нерэндомные, значит они повторяемые и токен можно в сессии не хранить.
Простой пример:
token=md5(date+user_id+userpass)
В течение суток данный токен будет валиден для пользователя user_id независимо от того, сколько сессий он откроет. Метку времени хранить при этом нигде не надо, как следует из псевдокода выше.
Я ответил на вопрос?
Что такое date? Текущая дата? Т.е. если пользователь сел писать статью в 23:59, то через минуту его токен протухнет?
Не придуривайтесь. Вы хотите чтобы я вам сделал «хороший образец плохого подхода»? Сдвиньте смену даты на пять утра, когда 99,9% населения спит, или привяжите токен к фазам луны, или к настроению своего кота — это ваши личные программистские трудности. Автор написал что так делать нельзя, я написал что так делать нельзя. Вы все равно просите сделать именно так и чтобы при этом вам это решение еще понравилось.
И да. Протухнет токен только в случае разрыва сессии.
>> Сдвиньте смену даты на пять утра, когда 99,9% населения спит или привяжите токен к фазам луны или к настроению своего кота

Статью может писать житель Калифорнии, у которого в этот момент 5 вечера.
В профиле пользователя можно указать его часовой пояс.
Вы пытаетесь придумать условия, в которых проявится недостаток нерэндомных токенов, при том что этот недостаток является фундаментальным для рэндомных. И ничего — как-то живут люди. Получают новый токен и отправляют форму повторно. Тоже самое будет и для нерэндомного, только во много раз реже.
Короче. Мне надоело с вами спорить. У вас сплошные костыли.
А я с вами и не спорил. Это вы мне хотели что-то доказать, даже не потрудившись прочитать что я вам написал.
Посмотрел сейчас как создаются токены в Symfony и Drupal. В обоих случаях использются сессии.
Э-э-э… а что вы ожидали увидеть?
Как раз это и ожидал. А что означает тройное «Э»?
Значит что я несказанно удивлен вашим комментарием. Так что даже не сразу нашелся что ответить.
Хм, а что в нём такого нескзанно удивительного? Вы считаете что не существует способов создать токен без использования сессий?
Не считаю. И не припоминаю чтобы я это утверждал. Или вам все равно с кем дискутировать на желаемую тему?
И не припоминаю чтобы я это утверждал.
Я не говорил что вы это утверждали.

Значит что я несказанно удивлен вашим комментарием. Так что даже не сразу нашелся что ответить.
Можете всё таки объяснить причину такого удивления?
Могу. Вот представьте, иду я по улице, вы ко мне подходите и заявляете:
— Я проверил под краном и в бачке унитаза — и там и там вода мокрая.
— Э-э-э… а какую воду вы там ожидали увидеть?
— Как раз такую и ожидал. А что означает ваше «Э»?
— Значит что я несказанно удивлен вашим комментарием.
— Вы считаете что вода может быть сухой?
— Не считаю. И никогда не заявлял подобного.
и далее по тексту.
Вот так я вижу сейчас наш диалог. Может вы меня с кем-то перепутали?
У вас в предпоследнем предложении диалога преднамеренная ошибка которая меняет весь его смысл. Я спрашивал полностью обратное. И аналогия с мокрой водой мягко говоря притянута за уши.

Кстати, вот пример генерации токена без использования сессии.
Вы пытаетесь мне доказать (а может опровергнуть — я так и не понял) то, чего я, по вашим собственным словам, не говорил (и я тоже помню что этого не говорил). Какое отношение это доказательство/опровержение имеет ко мне?
Не пытаюсь я ничего доказать, просто интересно стало почему вас так сильно удивил комментарий о том что Симфони и Друпал используют сессии для генерации токенов. Вы к сожалению так внятно и не ответили.
Он меня удивил, потому, что совершенно не имел отношения к обсуждаемому вопросу. Да и к самой статье он отношение имеет весьма далекое.
Хм, тогда вообще не понятно. Теряется логическая связь между токенами через сессии и мокрой водой в бочке.

Э-э-э… а что вы ожидали увидеть?
я несказанно удивлен вашим комментарием. Так что даже не сразу нашелся что ответить.
Я проверил под краном и в бачке унитаза — и там и там вода мокрая.

В итоге получилось всего лишь:

Он меня удивил, потому, что совершенно не имел отношения к обсуждаемому вопросу.


Ладно, возможно я действительно ничего не понимаю тут.
Каким образом можно осуществить CSRF атаку если все административные действия на сайте осуществляются только с помощью POST запросов?
Навскидку
1) Форма на другом сайте, подсунутая допустим админу.
2) Если есть допустим XSS, а она бывает часто, в связке дает возможность слать через Ajax запросы POST
Скорее всего есть и другие варианты
Впринципе первого способа достаточно, хотя с GET конечно все намного проще.
2) Если есть допустим XSS

Если есть XSS, то вся CSRF-защита улетучивается: можно и токен угнать и запросы слать с этой страницы (обход проверки реферера).
Навскидку
1) Привязка по IP
2) Куки недоступные из JS
Скорее всего есть и другие варианты
Привязка по IP не поможет. Недоступность куков из JS, конечно, спасет — но это отменит саму возможность ajax-запросов. А если разрешать ajax-запросы — то при наличии XSS злоумышленник воспользуется тем же самым механизмом.
Я в том смысле, что если есть Basic авторизация или проверка по IP в админку с куками мы не зайдем, нужно делать CSRF от админа с POST запросом со страницы на которой есть XSS.
А недоступность cookies из JS не отменяет остальные действия JS. Сейчас я говорю о флаге HTTPonly (https://www.owasp.org/index.php/HttpOnly)
А я в том смысле, что нет смысла делать CSRF от админа, если можно сделать XSS от админа.
Отошлю на сервер любой запрос, с IP адреса админа, с куками админа — и с авторизацией админа.
Для этого нужно, как минимум, физическое присутствие к компьютеру админа.
Не нужно. Достаточно XSS.
Специально для Вас сделал тестовую площадку, в хидерах браузерную защиту от xss отключил.
link
Получите, пожалуйста, через XSS секретную cookie с именем admin и приведите пример запроса.
Код страницы:

<?php header("X-XSS-Protection: 0"); ?>
<html><head></head><body>
<?php
setcookie ( 'admin' , 'secretToken' , 0 , '/', null,false , true );
setcookie ( 'notSecret' , 'ThisIsCookieNotSecretWithHttpOnly0' , 0 , '/', null,false , false );
echo $_GET['c'];
?></body></html>

То, что это и есть ничто иное как csrf
Нет, это XSS. Это было бы CSRF, если бы я сразу дал ссылку на slus.name/deleteallsite.php
Разница — принципиальная. Наличие активного скрипта на клиентской стороне делает защиту токенами бесполезной — злоумышленник вытащит его с первой же формы.
Похоже смысла спорить с Вами нет вообще, выше приведены примеры когда это не так. Csrf это не атака, это класс уязвимостей, как и xss. Использоваться они могут как совместно так и раздельное.
Пример сплойта для JavaScript, при ограничении по IP и Basic авторизация к папке admin и выставленном флаге HTTPOnly, при всех действиях в админке через POST
<script>
$.post(
  "/admin/do.php?act=add_user",
  {
    name: "admin",
    pass: 12345
  }
);
</script>

Данный пример именно связка XSS + CSRF, при необходимости добавляется допустим воровство токенов.

Приведите пример «можно сделать XSS от админа», без CSRF в таких же условиях, который может привести к компроментации приложения?
Единственный вариант на ум приходит, это фейковая страница.
При чем тут CSRF вообще? XSS может быть и хранимой, к примеру.
CSRF это отсутствие проверок при добавлении нового пользователя в данном примере через POST запрос.
Не важно в данном контексте активная или пассивная XSS.
А в чем техническая проблема?
Вы создаете на своем сайте страницу, где в скрытом фрейме загружается другая страница с формой. В этой форме все нужные данные для атаки вставлены в скрытые поля. А эта форма автоматически отправляется через JS после ее загрузки.
На своём проекте (asp.net mvc) сделал для защиты от csrf-атаки две вещи:

1) Все формы генерируются только через extension метод, который повторяет сигнатуру оригинального метода (Html.BeginForm), в нём инкапсулировано добавление токена (средствами самого asp.net, т.е. велосипед не строил)
2) Все ajax запросы идут через прокси функцию, которая к каждому post запросу добавляет токен (просто ищет его в форме и выдирает. подразумевает, что каждая страница, с которой может исходить post запрос имеет форму)

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

Если кому интересен код — пишите.
Я бы посоветовал еще и заменить стандартный хелпер Html на свою реализацию — чтобы никто оригинальный метод случайно не вызвал.

Это делается просто: находите в Web.config строку <pages pageBaseType="System.Web.Mvc.WebViewPage"> и заменяете базовый класс на свой, унаследованный от старого — и там переопределяете свойство Html. Не забыть сделать то же самое с типизированным вариантом.
Защита направлена на пресечение подмены лица, которое запрос шлёт. Чуть выше, в комментах, эта тема более детально раскрыта. Но в двух словах ещё раз: Если не использовать csrf токены, то любой POST/GET запрос может быть отправлен ВАМИ с чужой помощью. Т.е. вы просто открываете ссылку «котятки.рф/лалала» и всё, у вас удаляется сайт/перечисляются деньги на карточку/ставится статус в соц сети — всё что угодно. И самое главное — вы даже не заметите ничего, всё будет выглядеть до боли естественно. И хорошо, если сервер проверяет referer, но опять же, как писали выше — это не спасёт от чуть более продвинутой атаки с использованием flash старых версий.
Я его не пущу за свой компьютер! А на другом пусть открывает сколько влезет — у него будет другой токен.
Sign up to leave a comment.

Articles