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

Комментарии 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, так что если вы не настраиваете хедер в ручную, проблем у вас не должно быть.

А если Ajax + CORS?
>> Токены — обязательная защита от 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 без разницы.
А как быть если на странице используется какой-нибудь, прости господи, флеш? Что-то я сомневаюсь что он добровольно отправит правильные заголовки.
How can i send custom headers with URLRequest. Хотя там проблемы с Get вроде. Но тут уж только вариант — выносить его, либо делать отдельные эндпоинты, чтоб другие не страдали :)
Это если есть исходники flash-приложения. Если же нет, то все равно придется передавать токен через параметры. Значит в тот или иной момент он будет присутствовать внутри html страницы.
Вариант с заголовками хороший, но не универсальный. Понятно что эта неуниверсальность касается процентов 5 случаев, но нет гарантии что завтра заказчик не захочет именно эту экзотику у себя на сайте.
К сожалению не весь легаси может в токены :( Остается надеяться на более агрессивную политику вендоров браузеров.

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

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

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

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

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


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

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

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

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

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

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

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

Публикации

Истории