Как стать автором
Обновить

Комментарии 52

Шифрация/дешифрация ресурсоемкие операции. Гораздо проще использовать хэширование. В простейшем случае у нас будет код наподобие:
$time = time();
$token = md5(SECRET_KEY . $userData . $time) . ':' . $time;

Как проверить такой токен, думаю, понятно.
Боюсь, так можно достаточно быстро подобрать SECRET_KEY
Зря боитесь:
$time = time();
$secretKey = "33Sht?U<up-~f=>@xy8sah3uwA?T(<E8gE92vh5]rs4M3%-EbX,u9SqCk6jQ)}-J";
// $userData тоже может содержать неизвестные злоумышленнику данные. 
// Вы свой числовой id на хабре знаете?
$token = md5($secretKey . $userData . $time) . ':' . $time;
(да, мой числовой id 260599 :)
Хорошо, я забыл, что SECRET_KEY не пароль и может быть длинным)
Указанный способ работы с токенами часто применяется для генерации временной ссылки на CDN, например.
(да, мой числовой id 260599 :)

спасибо)
Лучше не стоит писать подобный код. Здесь может помочь злоумышленнику "Атака удлинением сообщения". То есть ваш secret_key просто «проигнорируется» при проверке.
Я, собственно, не для прода код писал, лишь обозначить алгоритм, но ваш комментарий, действительно, полезен в данном случае.
Flacker ниже уже указал на необходимость использования HMAC в подобных решениях.
Про md5
В связи с быстрой природой хэширующего алгоритма не рекомендуется использовать эту функцию для обеспечения безопасности паролей.
http://php.net/manual/ru/function.md5.php

Вместо md5 рекомендуется использовать password_hash
Любопытно за что минус-то?
Да, всё верно. Поэтому, надо четко понимать, где и что использовать. В случае подхода Encrypted Token шифрование обязательно. В остальных случаях нужно просто подписать данные. Для этого, может быть использован механизм HMAC.

Используемая хеш-функция — дело личное. От её выбора будет зависеть скорость генерации токена.
Конечно, при прочих равных, подписать быстрее, чем зашифровать.
Каждый раз читая такие статьи я все время прокручиваю у себя такой сценарий.
Предположим что я захожу на сайт good.com в результате которого мне в куки записывается номер сессии и csrf токен. Дальше я в новой вкладке открываю bad.com, который делает запрос к good.com и так как у меня в куках есть валидные номер сессии и валидный csrf, то запрос успешно выполняется.
Вопрос — где же тут защита?
Еще раз уточню: защищать необходимо небезопасные методы, к которым относятся: POST, PUT, DELETE, PATCH.

В целом, всё сводится к тому, что, кроме данных в сookies, необходимо послать данные о текущем CSRF-токене вторым путём. Это может быть post-data (для форм) или доп. заголовок.

Если вы говорите о подходе Synchronizer Tokens, то тут токен-эталон просто храним на сервере (в данных сессии), а токен-валидатор посылаем клиенту например в header или, если шаблон генерируется на сервере, прямо в html'e.

Т.е. при таком подходе явно хранить в cookie CSRF-токен не нужно.

Таким образом, в запросе с другого ресурса будет ID сессии, но не будет самого CSRF-токена, так как он не должен приходить внутри cookies.

P.S. Если не очень понятна идея, то говорите, не стесняйтесь, я попытаюсь написать подробнее.
Если страница вставлена в виде фрейма, или вообще просто вогнана в какой-то див с помощью ajax запроса (предположим что так позволит сайт сделать), и она генерировалась на сервере то bad.com получит валидную форму с валидным встроенным в html csrf токеном, и соответственно прекрасно сможет сделать POST.

Поэтому я и не понимаю данный вид защиты с помощью csrf токена хотелось бы узнать, где я ошибаюсь.

Пока я вижу только один метод — https плюс фиксация идентификатора сессии на идентификаторе самого https соединения. Тогда у нас на каждую закладку будет своя сессия и соответственно новая сессия в новом окне, плюс не нужно возиться с токенами.

Существует такая штука как Same Origin Policy. bad.com может сделать любой запрос к good.com — но он не сможет получить результат запроса.

Перечитав все, я наконец-то понял. Если на сайте bad.com вставлена СТАТИЧЕСКАЯ ссылка на good.com тогда csrf прекрасно сработает. Но извините, веб 1.0 умер лет 10 назад, и сейчас нет ни одного сайта без JavaScript. А если есть возможность сделать несколько запросов (форму и отсылку ее например) то csrf никак вам не поможет. Получается это какая-то защита от юных хакеров, которая еще и требует кучи дополнительного кода на стороне сервера, чтобы втыкивать этот токен во все места. Вывод — cors + https вполне достаточно.

CORS ограничивает только ajax-запросы, чего явно недостаточно. На сайте bad.com злоумышленник вставит <form action='good.com' method='post'> и инициирует отправку (сабмит) формы — CORS тут не поможет.

Автор пропустил Same-Origin-Policy. Если SOP настроен не правильно, на пример "Access-Control-Allow-Origin: *
" то любая CSRF защита будет в пустую. По-умолчанию SOP только Same-Origin, так что если вы не настраиваете хедер в ручную, проблем у вас не должно быть.

>> Токены — обязательная защита от CSRF.

Слишком категорично. Есть методы попроще.

1) Можно не использовать POST. Атаковать PUT, DELETE, PATCH невозможно без специальных условий.

2) «JS приложения обычно присылают XHR запросы с header (X-CSRF-Token), содержащим токен.». Если вы все равно требуете кастомный хедер от клиента, не проще ли просто принимать саму сессию в кастомном хедере, а не куки?

в чем проблема атаковать put, delete и patch? их точно так же можно послать используя js в браузере

CORS не позволит

а POST позволит?


ну и jsonp обходит cors же. нам же не ответ получить надо, а главное, чтобы действие исполнилось

POST позволит. jsonp обходит, но это и есть «специальные условия».
Сессия в куки должна быть httponly + secure. В скрипте не получить. Ну это ИМХО :)
>> Сессия в куки должна быть httponly.

А чего бояться? XSS? Ну если есть XSS, то у вас и так все плохо.
А чтоб не лазали куда не надо ;) Скрипт не должен иметь доступа к тому, чему не должен. В определенной ситуации может быть дополнительным фактором защиты.

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

“Проверяйте, но не полагайтесь только на X-Requested-With: XMLHttpRequest” — почему, если его можно поставить только через явное разрешение в CORS?

Присоединяюсь. Я довольно часто использую только проверку на X-Requested-With — дешево, сердито.
Чем это грозит?


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

На самом деле, OWASP подтверждает, что такая защита имеет место быть (раздел: Protecting REST Services: Use of Custom Request Headers)

Но есть уточнение, что такой вид защиты был уязвим с использованием Flash. И прилагается ссылка на репорт об уязвимости на Vimeo: https://hackerone.com/reports/44146

Таким образом, по версии OWASP, для обеспечения необходимого уровня защиты необходимо дополнительно проверять заголовки Origin и Referer (впрочем, как и при любом другом методе).

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

Другой вопрос, а у всех ли пользователей софт действительно новейший… (http://caniuse.com/#search=CORS)

В интернетах можно увидеть разные мысли на тему такой защиты.

А по поводу CORS, нужно понимать, что сам по себе он не предоставляет защиты от CSRF. Вот, например, ответ на SO о том, почему CORS не совсем верно интерпретируют: http://security.stackexchange.com/a/97938
Если использовать Double Submit Cookie, то менять куку в другой вкладке — опасно. Запросы из первой вкладки сломаются и клиенты начнут ругаться. А не менять или менять «раз в час» — как и без защиты толку не добавит. Или я ошибаюсь?
Да, именно так. Вы всё верно поняли.

В статье предложил, как можно синхронизировать:
Синхронизация токенов между табами может быть реализована с использованием localStorage и его StorageEvent
Если вы пишете свой Web-Сервис в соотвествии со стандартом RFC7231, то ...

… то надо отказаться от любых кук. Авторизацию нужно проводить через выдачу токена, который в дальнейшем должен добавляться ко всем запросам как параметр.


В таком случае можно забыть про CSRF, XSS и другие страшные слова, а также смело прописать Access-Control-Allow-Origin: *. Если у злоумышленника нет токена — он ничего не сможет сделать. А если у него есть токен — значит, он никакой не злоумышленник, а валидный юзер.

Авторизацию нужно проводить через выдачу токена, который в дальнейшем должен добавляться ко всем запросам как параметр.

А идентификатор сессии разве не является подобным токеном?
Полный токен содержит набор данных по которым можно провести аутентификацию и авторизацию. В id сессии этого нет и вам придется разыменовывать этот id для получения информации. Если у вас не монолитное приложение — это отдельный геморрой :)
Хотя по тому же OpenID Connect можно использовать ReferenceTokens + интроспекцию.

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


Если не завязываться на магию кук в браузере — то и вектор атаки пропадает.

Ну если вспомнить недавнее прошлое, то идентификатор сессии так же подклеивался к GET запросам. И все понимали какая это дыра в безопасности. Если я правильно понимаю, то вы предлагаете тоже самое делать с токеном?
Тут речь о простом Bearer токене в заголовке. Http verb без разницы.
А как быть если на странице используется какой-нибудь, прости господи, флеш? Что-то я сомневаюсь что он добровольно отправит правильные заголовки.
Это если есть исходники flash-приложения. Если же нет, то все равно придется передавать токен через параметры. Значит в тот или иной момент он будет присутствовать внутри html страницы.
Вариант с заголовками хороший, но не универсальный. Понятно что эта неуниверсальность касается процентов 5 случаев, но нет гарантии что завтра заказчик не захочет именно эту экзотику у себя на сайте.
К сожалению не весь легаси может в токены :( Остается надеяться на более агрессивную политику вендоров браузеров.

Дырой в безопасности это становится когда идентификатор сессии оказывается в адресной строке. Для AJAX-запросов такой проблемы нет.

С XSS спорно. Если еще CSP докинуть сверху — то вполне.

О каком CSP в веб-сервисе речь? CSP применяется в html, а веб-сервис должен работать только с json или xml.

А о каком CSRF тут обсуждение идет если это просто сервис? :) Вроде как обсуждается связка — страница в браузере + REST сервис.
А о каком CSRF тут обсуждение идет если это просто сервис?

Вот именно это я у вас и спросил!


Вроде как обсуждается связка — страница в браузере + REST сервис.

В случае когда страница на сервере — статическая и вся динамика реализуется на стороне браузера, CSP при прямых руках не требуется (но и не помешает).

Обсуждение CSRF сразу подразумевает наличие веб страницы. Иначе смысл всего этого топика :)))

Если у вас только статика, если вы контролируете целостность ресурсов, тогда нет вопросов.
Уникальный токен для каждой операции
Действует единожды

Я правильно понимаю, stateless решения не удовлетворяют этим требованиям?
И насколько вообще эти требования критичны?
Зависит от реализации. Stateless решения вполне могут удовлетворять всем требованиям.
Например?

Кто бы перевел всю статью на OWASP, было бы круто

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории