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

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

К слову, уже есть rc версия под .NET Core 2.0, включая пакет для работы с EF Core (тоже 2.0). Только валидацию токенов на стороне API ещё не завезли, так что OpenIdConnect и JWT-only (их middlewar'а даёт интроспекцию и возможность юзать Reference-токены)

Спасибо за замечание. Статья писалась в свободное от прочей работы время, затем некоторое время болталась на модерации, я не заметил, что Брок запушил апдейт Quickstart UI в сентябре.

А чем это все лучше стандартной авторизации кукой, которая по сути готова изкоробки?
Понимаю, что даром бы вот это все не городили, но все же? Централизированная аутентификация для разных продуктов, большая секурность (на основе чего?)?

Я бы не сказал, что токены "лучше" или "хуже", у них есть свои области применимости, как и у кук. Например, HTTP-only Secure куки признаны одним из лучших механизмов хранения сессии. IdentityServer4 хранит сессию пользователя именно в куке по умолчанию.
Для того, чтобы толково ответить на Ваш вопрос понадобится написать немаленькую такую статью, не думаю, что это хорошая идея, тем более, что многие это уже сделали.
Вот тут, если пролистаете до реального описания кук и токенов, может быть интересно почитать, например.


Если совсем коротко, то мне на ум приходят такие варианты.


  1. Требуется выдавать "токены" одним центром авторизации для многих ресурсов. Возможно, это даже не ваш центр авторизации (Google, Facebook, Auth0 и т.д.).
  2. Требуется логика, которая сильно выигрывает от stateless-механизма. Как тут в примере про отель.
  3. Проблемой для реализуемого сценария является сам протокол кук, и связанная с ним специфика вроде отправки кук при каждом GET-запросе по умолчанию вообще, и CSRF-уязвимости в частности.

Ну во-первых, ни моя статья, ни мои комментарии не содержат предложений использовать JWT для сессий.


Во вторых, статью я читал, она ИМХО построена по старому-доброму принципу "вначале хорошенько передёрнуть, потом всё эмоционально развенчивать". Нормальное обсуждение статьи тут. Если любите такой жанр, можете почитать ещё эту.

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

Правильно говорить не "старые добрые сессии оказываются не хуже", а "старые добрые куки оказываются не хуже для реализации сессий".


Если задача заключается не в реализации сессий, а в защите доступа к API — то токены справляются даже лучше кук, потому что их не надо дополнительно защищать от CSRF-атаки.

Но надо защищать от кражи из LocalStorage сторонними скриптами, подключенными на странице. И мне что-то не приходит в голову, как.

А еще, основная фишка токенов – stateless аутентификация. Но она получается не на столько секьюрной как stateful с помощью сессий.

Куки тоже от сторонних скриптов не спасают. Да, нельзя украсть http only куку — но никто не мешает прямо со страницы делать запросы. Иными словами, успешный XSS проламывает любую защиту.


Если же хочется защититься именно от кражи для повышения порога атаки — токен можно привязать к Origin.

А как привязать токен к origin? Идея действительно интересная.
Кто помешает использовать украденный токен потом вообще из curl?

Никто не помешает, что-то я забыл про эту утилиту и глупость сморозил.

Да, про stateless аутентификацию. Она делается не с любыми токенами, а только с подписанными (например, с JWT). Но не надо думать, что только токены можно подписывать — с куками такое тоже можно провернуть. И недостатки у них будут общие — отсутствие возможности логаута.

Да я вообще не противопоставлять куки и токены. JWT можно и через куки слать.
А подписанная кука уже сама по себе токен. Я в основном про stateful/stateless.
Вот не в первый раз встречаю утверждение, что приложения на токенах не нужно защищать от CSRF.

Тут внезапно не всё так очевидно. Вообще говоря, приложения, которые получают доступ к ресурсу на основе Bearer-токенов обычно защищённее, т.к. атакующий не может полагаться на стандарт раз, и access_token'ы обычно делают живущими недолго два. Однако, если у вас SPA с восстановлением текущего состояния из URL, при атаке через CSRF может так случится, что приложение прекрасно восстановит контекст из URL и localStorage, а затем сделает нужный хакеру запрос.

Так что такие приложения, вообще говоря, труднее атаковать — да, но защищать именно от CSRF-атак их тоже нужно.

Это вы уже описали атаку на приложение, а не на API. Соответственно, и защищать от такой атаки нужно само приложение.


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

Это вы уже описали атаку на приложение, а не на API. Соответственно, и защищать от такой атаки нужно само приложение.

Есть такое.

По остальному согласен. Ну и anti-CSRF-токены различные.

А вот anti-CSRF-токены как раз тут и не помогут. Потому что чтобы такой токен работал — его надо будет запихнуть в строку адреса, но это убьет саму идею адресации: такой защищенный токеном адрес нельзя будет ни другу передать, ни в закладки добавить. Проще сразу делать SPA без роутинга и игр с URL.

Ну например на форме скрытое поле с anti-CSRF-токеном, выданным сервером. И делать shareable полузаполненную форму смысла большого нет, и проблема с CSRF постинга форм решена.

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

Но постить-то заполненную сможет, при помощи специальных утилит, например. Или вот заходит пользователь на какой-то сайт, а там появляется popup с формой, и форма отправляется, за долю секунды. Или даже такое.

Я уже писал, от XSS не спасает ничего.

Я привёл примеры именно способов осуществления CSRF-атаки, у самого приложения собственных XSS-уязвимостей может и не быть. А для CSRF javascript и social engineering применять не запрещено.
Тут терминологическая путаница.
«Self-XSS» — это, строго говоря, не совсем XSS, это гораздо больше и вообще о другом. Это выполнение произвольного кода в консоли. Этот код может, например, выполнять GET, а затем POST-запрос. С точки зрения вектора атаки на приложение — это будет CSRF. Да, можно было бы попытаться провести XSS самого сайта (но сложнее т.к. проще поместить «рекомендации» по действиям с консолью на другой сайт). Ваш сайт даже может выводить предупреждение в консоль, как Фейсбук, ему это не поможет.
TL;DR; Мой пример не про XSS.
Впрочем, да, в формулировке последнего комментария, от любого Self-XSS, anti-CSRF-токен не защищает.

Меня смутило «Я уже писал, от XSS не спасает ничего.» по поводу CSRF-примера.
Ну и в догонку: я правильно понял, что в своих SPA Вы anit-CSRF токены не используете?

Тут есть общий момент с XSS — выполнение произвольного кода в контексте страницы. Этого достаточно для того чтобы обойти анти-CSRF проверку, потому что у злонамеренного кода есть полный доступ к состоянию вашего приложения, в том числе к любым секретным токенам.


Токен генерируется сервером? Отлично, попросим сервер сгенерировать нам токен.


Токен записан в LocalStorage? Отлично, прочитаем его оттуда.


Токен записан в локальную переменную в замыкании? Отлично, переопределим fetch или JSON.stringify и дождемся когда приложение само отдаст нам токен.

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

Только вот когда это читаешь, оторопь берет – неужели вот это ВСЁ нужно только для обеспечения аутентификации / авторизации. Бедные новички!

Да, судя по документации, если взять IdentityServer4, то не понадобится больше вообще ничего. Тут тебе и SSO, и разные типы клиентов, и аутентификация между разными микросервисами.

Но для простых приложений, бывает быстрее накидать свою application-specific схему авторизации. Просто на основе базовых знаний протокола HTTP и известных уязвимостей. Чем разбираться со всеми этими claims, issuers, audiences, access-refresh tokens etc.

Возможно, но я лично придерживаюсь в этом вопросе принципа "never roll your own".

Это на самом деле абсолютно верный принцип. Но тут у меня претензии не к самим протоколам и существующим реализациям, а к их документации.


Все туториалы вываливают сразу на человека кучу инфы. Я еще не видел, чтобы гайд по аутентификации был построен так:


  • Вам нужна простая авторизация – вот минимальный набор действий.
  • У Вас несколько приложений – настройте еще это и это.
  • Нужно SSO – добавьте вот такую опцию.

Почему, например, имея ровно один сервер, и ровно один браузерный клиент, я должен указывать какие-то Issuer и Audience? И еще куча неочевидных моментов.


И за всем этим очень трудно уследить. Для каждого протокола авторизации есть несколько версий. Для каждой версии – несколько реалкизаций со своими версиями. У каждой версии реализации новый формат конфигурации.

Почему, например, имея ровно один сервер, и ровно один браузерный клиент, я должен указывать какие-то Issuer и Audience? И еще куча неочевидных моментов.

А зачем вам SSO с одним сервером и одним браузерным клиентом? Для таких случаев существует ASP.NET Identity


Или даже что-то свое на формах слепить можно

Или даже что-то свое на формах слепить можно

Гм. Но я же именно это и написал в изначальном комментарии :)

И есть еще один момент. Когда я настраиваю авторизацию по туториалу, меня не покидает ощущение – "а все ли я сделал правильно"? Я выполнил какие-то действия без понимания, как каждое из них влияет на безопасность приложения. Защищает ли описанная в туториале конфигурация именно от тех видов атак, от которых я планирую защитить приложение?


Или после каждой настройки аутентификации нужно проводить пентест? Так мы никогда не сможем начать наконец работу над бизнес-логикой проекта. Ведь авторизация настраивается как правило вначале.

  1. В принципе, ощущение игрушечности существующих туториалов и было одной из мотиваций того, что я написал yet another. По моей задумке, он должен быть в этом смысле лучше.
  2. Вы в любом случае будете делать пентесты, скорее всего ещё и будете использовать автоматические.
Тут наверное вообще присутствует фундаментальная проблема туториалов.
В любом туториале (не обязательно по безопасности) рассказывается КАК сделать что-то.
А вот ЗАЧЕМ делать что-то не рассказывается, потому что без этого приложение как правило не будет работать вообще. Но вот с уязвимостями как раз нет очевидных вещей.

Например, если не прописать connection string – то мы не сможем подключиться к БД.

А если не проставить в авторизационный токен expiration time, то приложение будет работать? Да. Но появится уязвимость.
В статье перепутаны местами значения по умолчанию для AccessTokenLifetime и IdentityTokenLifetime
Поправил, спасибо!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории