fa
Feedback
401 Unauthorized: аутентификация и не только

401 Unauthorized: аутентификация и не только

رفتن به کانال در Telegram

Канал про IAM и все, что рядом: - аутентификация - session management - access control Возможны также посты про API и InfoSec Чат: @unauthz401 Автор: @andreukuznetsov

نمایش بیشتر
349
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+47 روز
+2430 روز
آرشیو پست ها
Многие читатели канала наверняка хоть раз использовали сервис jwt.io для декодирования JWT. Я и сам им частенько им пользуюсь
Многие читатели канала наверняка хоть раз использовали сервис jwt.io для декодирования JWT. Я и сам им частенько им пользуюсь, удобно. Так вот, разработчики решили провести его модернизацию и в качестве беты уже выкатили новую версию. Для доступа к ней нужно: - зайти на https://jwt.io/beta - ввести пароль jwtv2 Из интересного отмечу, что здесь разделили функции decoding и encoding на два разных интерфейса. Почитать про изменения подробнее или оставить фидбек можно в данном issue на гитхабе.

Третья часть обзора книги "Solving Identity Management in Modern Applications: Demystifying OAuth 2.0, OpenID Connect, and SAML 2.0 (2 издание)" Здесь включены оставшиеся главы с 13 по 22: - Logout - Account Management - Deprovisioning - Troubleshooting - Exceptions - Less Common Requirements - Failures - Compliance - Looking into the Crystal Ball - Conclusion https://telegra.ph/Obzor-knigi-Solving-Identity-Management-in-Modern-Applications-Demystifying-OAuth-20-OpenID-Connect-and-SAML-20-CHast-3-Glavy-9--01-05

Видео доклада "Supercharging OAuth 2.0 Security" от Philippe De Ryck с DevFest 2024 в Копенгагене. Не так давно на канале NDC Conferences (очень классный канал, советую) была опубликована запись данного выступления. Вообще Philippe De Ryck, насколько я встречал его работы, специализируется больше на безопасности клиентских частей веб-приложений в контексте вопросов аутентификации. Это можно увидеть из его блога или соавторства в драфте OAuth 2.0 for Browser-Based Applications. В этом же материале речь идет о некоторых общих концепциях в целом. Автор предлагает посмотреть на "относительно недавно появившиеся возможности OAuth", рассмотрению которых доклад и будет посвящен. ➡️Так упоминается RFC 8707 Resource Indicators for OAuth 2.0 для ограничения области действия access token, отмечаются особенности работы подхода с точки зрения получения новых токенов на стороне Cilent. ➡️Поднимается вопрос обеспечения безопасности для Authorization request, а особенно обеспечения его целостности (integrity) и защиты от манипуляций (tampering). В этом контексте вспоминаем про JWT-Secured Authorization Request (JAR) и Pushed Authorization Requests (PAR). Для JAR параметры Authorization request оборачиваются в специальный JWT, подписанный на стороне Client. Соответственно в issuer фигурирует client_id, а audience является уже сам Authorization server. При выполнении самого Authorization request вместо использования query-параметров в plain-text передается как раз созданный JWT. В случае PAR в Authorization request параметры не передаются вовсе, а передается только специальный идентификатор, соответствующий предварительно созданному набору параметров Authorization request. ➡️Говорит автор также и про извечные проблемы bearer-токенов и не менее извечное движение к sender-constrained токенам доступа. Кратко рассматривается использование основных proof-of-possession механизмов: mTLS и DPoP, справедливо отмечая, что реализация данных подходов может быть не так проста и не всегда оправдана. В общем и целом легкий обзорный доклад без глубокого погружения в нюансы. Если интересно начально узнать об упомянутых возможностях и механизмах и посмотреть демо их работы, ознакомиться может быть полезно.

Обзор книги "Solving Identity Management in Modern Applications: Demystifying OAuth 2.0, OpenID Connect, and SAML 2.0 (2 изда
Обзор книги "Solving Identity Management in Modern Applications: Demystifying OAuth 2.0, OpenID Connect, and SAML 2.0 (2 издание)" Я долго с грустью полагал, что для области IAM мало действительно хороших книжек, которые в том числе можно было бы порекомендовать. Безусловно, качественные материалы встречаются, но чаще я находил их в разрозненных представлениях, нежели собранными в одном произведении. Книгу «Solving Identity Management in Modern Applications» я видел еще давно, но почему-то сложил о ней начальное впечатление, что она, как и некоторые другие, подает переписанную с неточностями информацию из стандартов и спецификаций, поскольку на обложке как раз стандарты и указаны. И вот всегда я обходил ее стороной. Однако в прошедшем году наткнулся в одном из чатов на положительный отзыв, просмотрел оглавление и решил прочесть. Что могу сказать... Это книга, которую можно посоветовать как некий комплексный материал. Причем как «новичкам», так и тем, кто уже смешарик продвинутым. Я сам удивлен, что мне настолько она откликнулась. Самое удивительное, что именно в книге, которую долго откладывал в сторону, я нашел очень большое количество мыслей, которые даже выражены буквально так же, как их формулирую для себя сам, и которые в таком виде почти нигде не встречал. Большое количество моментов пересекаются с темами планируемых постов для канала, которые я себе выписывал, так что о чем-то уже можно и не писать, а просто отсылать к её разделам. Еще мне очень понравились многие формулировки в книге, поскольку они в своем большинстве достаточно точные и не создают путаницы. Увы, во многих современных статьях с формулировками прям большая беда, из-за чего часто смысл искажается, и читателям становится сложнее понять оригинальную идею. Так что теперь смело рекомендую эту книгу к прочтению. Это гарантированно будет во много раз полезнее, чем читать многочисленные статьи с хабра «Об OAuth и OIDC простыми словами», где, увы, в основном ошибка на ошибке. Я даже решил написать для книги отдельный развернутый обзор. Но это не будет кратким содержанием или пересказом, а скорее выделением важных и полезных моментов, которые нашли во мне отклик. Как пример, в обзоре включаю цитаты, которые выделял при прочтении. Формат получился экспериментальный, но для меня лично это был скорее "диалог с книгой", как продолжение красивой русской традиции. Я честно старался искать и места для критики, но найти таковых получилось довольно немного. Но, имхо, чтобы стать таким же довольным, читать стоит прям максимально внимательно и вдумчиво, потому что порой невероятно полезная мысль может быть выражена одной строчкой или даже присутствовать между строк. Кстати, авторы даже предлагают подходы к порядку прочтения для тех, кто не любит читать главы по порядку:
We recommend reading the chapters in order, at least through Chapter 15, as many of these chapters build on previous chapters. For the rebels in the crowd, we especially recommend at least reading Chapters 4 through 10 in order as they have the most dependencies on earlier content. The chapters after Chapter 15 can mostly be read in any order. Chapter 16 on troubleshooting will be most relevant when you need to debug an issue. Chapter 18 on less common requirements might be valuable to read early on in a project as it may help you identify items to include in your project plan. Chapters 17 and 19 cover different types of issues and will help you plan for, or avoid, many mistakes.
Обзор разбит на 3 части, которые полностью покрывают содержание и будут опубликованы в течение некоторого времени. Здесь же представляю первую часть обзора, которая доступна по ссылке ниже. Остальными ссылками пост будет дополнен позже. Часть 1 (главы 1-7) | Часть 2 (главы 8-12) | Часть 3 (главы 13-22)

Статья "Ory Kratos — конструктор для сборки цифрового продукта любой сложности" Интересная статья про возможности Open Source продукта Ory Kratos. Жаль, конечно, что описана работа в вакууме, а не на реальном кейсе. Вообще материалов по опыту использования стека от Ory мало, хотя знаю, что и в РФ есть компании, кто у себя использует или планирует к использованию, но делиться кейсам пока не хотят. Поэтому довольствуемся чем есть, буду надеяться, что авторы в будущем поделятся и опытом с реального проекта, по крайней мере в статье пишут, что прям реально "используют" где-то.

Видео "JWT vs. mTLS for service-to-service authentication" от solo.io Интересное видео про различные способы межсервисной аутентификации. Обозначают основную проблематику: "Как сервису B достоверно определить, что к нему действительно обращается сервис A?" и дальше идут от этого. Ребята рассматривают 3 подхода: 🔵OAuth 2.0 access token в виде JWT 🔵самоподписанный JWT на уровне сервиса 🔵mTLS с использованием SPIFFE При этом говорят, что стараются не рекламировать и продавать какой-либо из них, а скорее рассказать "а как бывает" и подчеркнуть, что простора, где можно ошибиться, хватает. Поднимается важность безопасности при разработке, несмотря на частое желание "to stay away from it". При этом все равно отмечают, что атака может быть проведена не только с использованием какой-то супер-крутой 0-day уязвимости, но и как результат эксплуатации цепочки, состоящей из того, что по отдельности может казаться мелочами ("Death by a Thousand Cuts"). Упоминается и использование API GW как централизованного места, на которое можно "сгрузить" часть security-фичей, чем обеспечив более гарантированное и консистентное их применение для всех сервисов. Но логично подмечают, что такой подход идет преимущественно для north-south взаимодействий, и особо не применяется для east-west, особенно если API GW находится где-то снаружи или далеко от окружения, где развернуты сервисы. Вообще выделяется важность токенов, говорят о том, что это те же секреты, дающие возможность доступа, и нужно думать об ограничении и их времени жизни, и области применения. Для кейса с access token очень правильно поднимают вопрос важности audience для токенов, который тоже порой хотелось бы слышать почаще. Что, мол, не все вообще используют audience, а если использовать "по уму", тобишь иметь различные токены для различных audience, то получаем необходимость сервису иметь кучу токенов, которые еще и надо все время обновлять. Для случая с самоподписанными JWT справедливо говорят, что это будете работать на паре сервисов, но на большем количестве вы получите геморрой с поддержкой такого решения, обеспечением взаимного доверия и ротацией ключей. Отмечают, что mTLS при корректной реализации позволяет закрыть не только задачи шифрования трафика, но и как раз аутентификации между сервисами. При этом акцент делают на реализации с применением service mesh, поскольку в таком случае на mesh, опять же, возможно "сгрузить" ряд универсальных для всех сервисов фичей, в том числе по работе с сертификатами. По словам авторов такой подход дает больше уверенности платформенной команде, что функциональность, касающаяся безопасности, реализована у всех и одинаково. Отдельно обращу внимание, что кратко упоминается любопытный поинт о том, что в концепции SPIFFE вообще-то SPIFFE Verifiable Identity Document (SVID), который является сущностью, подтверждающей Identity вызывающей стороны, может быть не только в виде X.509 сертификата, но и в виде JWT. И теоретически это может работать, но реализаций таких авторы пока не встречали. Отличие, согласно видео, здесь в том, что в случае с сертификатом сам "секрет" никуда не отправляется и не покидает пределов самого сервиса, который его получил. Используется только публичный ключ. А вот JWT, который сам по себе уже чувствительный, нам бы уже пришлось отправлять, как говорится, "по сети". В общем видео показалось интересным и полезным, ребята говорят толковые вещи.

SSO в мобильных приложениях Я не большой мастак в вопросах разработки мобильных приложений, с вебом доводилось работать куда больше. Однако стало интересно, а какие подходы сейчас используются, например, когда у компании несколько приложений и нужно организовать SSO для них. 1. Стандартный SSO с использованием инстанса браузера Самый простой и известный способ заключается в использовании какого-либо инстанса браузера: открыть полноценный браузер или "встроенное" в приложение окно, пользователь первично аутентифицируется, в браузере сохраняется SSO-cookie. При последующих входах в приложения повторный ввод учётных данных не потребуется, пока SSO-cookie жива и валидна. Однако здесь есть подвох в том, что у разных инстансов браузера могут быть разные cookie jars, которые не шерятся между ними. Пример 2. Подход с диплинками Здесь суть в наличии некоего "мастер-приложения". После входа пользователя в него, прочие приложения могут направлять Authorization request не через браузер, а диплинком в это приложение. Мастер-приложение открывается, пользователь сначала подтверждает вход в него, чаще всего через API ОС биометрией или пин-кодом, а затем уже в нем подтверждает вход в другое приложение. То есть consent screen отображается средствами мастер-приложения. Далее переход по redirect_uri может быть выполнен также диплинком обратно в приложение. Это можно увидеть в некоторых приложениях экосистемы Сбера или приложениях, использующих вход через Госуслуги. Пример 3. OpenID Connect Native SSO for Mobile Apps Однако есть ещё способ, при котором приложение "само" может узнать вашу Identity без перехода в браузер или другое приложение. В данном случае в Authorization request запрашивается специальный scope "device_sso", что позволяет в Token response получить в том числе и device secret. Это новое понятие:
Device Secret - Opaque value to the client, issued by the Authorization Server, that uniquely identifies the device instance and provides credentials for the device.
The device secret contains relevant data to the device and the current users authenticated with the device.
Приложение далее сохраняет ID token и device secret в хранилище через API операционной системы, например, Keychain в iOS. Задумка здесь в том, что приложения, подписанные одним издателем, могут пошарить между собой определённые данные через защищённые хранилища ОС. Так последующие приложения (того же издателя) смогут извлечь сохраненные ID token и device secret, а далее предлагается утилизировать Token Exchange. Приложение 2 отправляет Token Exchange Request, где передаёт ID token и device secret, и в ответ может получить токены для себя. Кроме этого, технически возможно также пошарить состояние аутентификации и между устройствами. Вот здесь предлагают подход с cross-device SSO на базе того же iCloud Keychain. Спецификация достаточно новая и имеет свои особенности: например, использование ID token с истекшим сроком жизни, а также некоторую путаницу с audience у того же самого токена. На эту тему есть ещё выступление Frictionless Authentication with Mobile Single Sign-On (SSO) Докладчик рассказывает как раз про проблемы SSO через cookie в браузере на iOS-устройствах, говорит, что там разные инстансы браузера, между которыми cookie не шарятся. Также кратко показывает пример cross-device SSO. И можно посмотреть, как это реализовали те же Okta: https://developer.okta.com/docs/guides/configure-native-sso/main/

Код 401 Unauthorized не про авторизацию Код и название данной ошибки отражены в названии канала. Unauthorized вообще можно перевести как "неавторизованый". Однако интересный парадокс в том, что в современном вебе 401 ошибка говорит не об ошибке авторизации, а об ошибке аутентификации. Такую ошибку чаще всего вернем, если аутентификация запроса была неуспешна. Например, если в запросе отсутствовали необходимые учётные данные (некий токен, sessionID) или проверка их валидности была неуспешной. При этом для ошибок уже авторизации встречаем как раз код 403 Forbidden. Данный код обычно возвращаем, если с переданными учётными данными к запрашиваемому ресурсу/операции нет доступа. То есть аутентификацию запроса выполнили, но вот доступа уже нет. Было интересно узнать, если какая-то история под данным "исторически сложилось". Дошел до первого RFC для HTTP/1.0 - RFC 1945 - однако ничего интересного, увы, не нашел. Вероятно, идея была связана с тем же заголовком Authorization, используемым для целей аутентификации. Конечно, семантика использования "правильных" и "неправильных" кодов ответа при проектировании API - это большой многолетний холивар. Сюда же можно вспомнить подход, когда порой мы не хотим даже показывать факт существования какого-то ресурса без необходимых прав доступа, и в ответ на запрос будем и вовсе возвращать 404.

Как не путать один JWT с другим JWT бывают разные, и используются они для разных целей. Чтобы токен не мог быть использован не по его прямому назначению, важно уметь отличать JWT разных типов. Особенно это касается проверки токенов доступа - access tokens. Самый простой пример можно встретить в том же OIDC. Допустим, используется как ID token (всегда JWT), так и access token тоже в виде JWT. Логично, что важно сделать так, чтобы ID token даже при весомом желании не мог быть принят и обработан в качестве access token. Задача известная, и есть разные варианты ее решения. 1. Использовать значение клˈейма (claim) "aud". Поскольку разные типы токенов предназначены для разных получателей, то можно воспользоваться одним из стандартных клеймов JWT. Если использовать его по назначению, то в нем должен быть идентификатор стороны-получателя. У разных типов токенов получатели разные, что позволяет понять, когда токен предназначен не для нас 2. Взаимоисключающие проверки клеймов, входящих в состав токена. Вполне логично предположить, что разные типы JWT будут обладать различным набором клеймов и/или иметь различные их значения, поскольку служат разным целям. Тогда правила проверки каждого типа токенов могут быть взаимоисключающими, чтобы токен типа 1 не проходил проверку валидности при попытке его использования как токена типа 2 и vice versa. Это может быть выражено в составе клеймов: например, в системе JWT типа access token должен всегда иметь клейм "scope". А другие виды токенов - не должны. Так условный Resource server, если ему попробовать скормить JWT другого типа, при проверке обнаружит, что обязательный клейм scope отсутствует, и токен проверку не пройдет. Также это применимо не только к составу клеймов, но и к их значениям. Сюда же можно отнести подход, когда используются специальные клеймы, в которых явно указывают тип или назначение токена. Например:
"purpose" : "access_token" // для access token
"purpose" : "id_token" // для ID token
3. В продолжение п.2 есть еще смежный подход Explicit typing, тобишь явное указание типа. Подход утилизирует для этого стандартный параметр "typ" заголовка (header) JWT. Тогда значение для access token может выглядеть, например, так: "typ" : "at+JWT", а для ID token уже как-то вроде "typ" : "id+jwt". В том же JWT Profile for OAuth 2.0 Access Tokens тоже упоминают про такой подход и как раз предлагают ввести тип "at+jwt" 4. Также можно использовать различные ключи для подписи токенов разных типов. Однако тут есть нюанс. Типовая проверка подписи выполняется так: сторона-получатель достает идентификатор ключа, использованного для подписи, из параметра заголовка "kid" и далее смотрит, а есть ли публичный ключ с таким key ID в ответе от нужного JWKS URI. Из этого следует, что если мы будем публиковать публичные ключи для проверки разных типов токенов JWT на одном JWKS-ресурсе, то при проверке все ключи будут успешно получены из этого JWKS, что сделает проверку подписи любого токена успешной. Поэтому здесь я не знаю подхода умнее, чем использовать для таких случае разные JWSK URI, если необходимо повысить гарантии, что один токен не будет принят за другой. Вести белый список разрешенных kid может быть затруднительно, поскольку ключи ведь нужно иногда ротировать. Полезно отметить, что лучше подумать о такой задаче еще на стадии проектирования структуры ваших токенов и при определении перечня рекомендуемых/обязательных проверок для Resource servers. Явно указание типа (любым способом) и взаимоисключающие проверки хорошо для этого подходят. А если структура и подход с проверками были спроектированы так, что токены надежно не отличить, то останется использовать только разные ключи. Также, конечно, может использоваться и комбинация подходов: например, проверять значение клейма aud (и осмысленно заполнять его) имеет смысл вне зависимости от того, используются ли другие подходы.

Есть еще интересный нюанс. В п. 7.1 RFC 9636 мы видим предполагаемую возможность пользователю в процессе согласия указать или уточнить какую-то информацию, изначально не входившую в authorization_details. Например, выбрать конкретный счет, к которому хотим предоставить доступ, из списка. Учитывая, что экран согласия обычно находится в ведении AS, появляется вопрос: откуда у AS возьмется информация о тех счетах, которые имеет пользователь? Неужто AS должен сам это знать? Не найдя однозначного ответа, решил обсудить вопрос с авторами самого RFC. 🟡Сам ответ, "как" AS получит какие-либо данные для отображения пользователю лежит за рамками данного RFC, и какого-либо референтного подхода тут, увы, не предполагается 🟡Задумка меня пока смущает. Не вижу до конца, как "красиво" реализовать пусть тот же самый пример с выбором счета, к которому надо предоставить доступ. То есть придется либо запрашивать данные по ним от лица самого AS, либо, допустим, асинхронно обогащать данные по пользователям, хранящиеся на стороне AS, краткими данными по списку их счетов (пусть маскированными представлениями). Но вот перспектива примешивать в AS какие-то бизнес-данные пока что-то не радует 🟡Зато мне любезно предоставили один из примеров, как это реализовано. Надеюсь, будет вполне этично поделиться здесь:
In Authlete we’ve got three components working together — an IdP (the AS), an Authlete Server (the RS), and the console (the client). When the client goes and talks to the AS, the client doesn’t know which set of services the user has access to, so it can’t ask for a detailed list of rights from the AS since it has no idea who the user is let alone what that user can access. The client at this point doesn’t even know where the RS’s are — since in our environment, each installation of the RS is managed separately for access rights. So the client just asks for a basic scope, "authlete", that just tells the RS "anything on the authlete APIs that this user can do", which is appropriate for a client to be able to ask for. The AS knows which paid and free accounts the user is attached to, and therefore which services on which Authlete Server installations the user has access to. So when the AS makes the token, it puts that information into the token’s metadata (we use introspection in our case). The client in our case doesn’t read the RAR data back itself, it learns of which RS’s that it can call through an API call back to the AS. The RAR object captures which services on which RS’s the client can do which actions to. When the token gets used by the client to call the RS, the RS doesn’t know anything about the user or accounts that went into making the token, but it doesn’t have to. The RAR object on the token simply lists out which services the client can access and for what purposes, so it’s a really easy and simple check. And that same token can be targeted to multiple RS’s, like if the user’s got access to a Dev and Prod environment on different servers. This lets us keep our user data in one place instead of spreading it out across multiple RS’s. And the RS’s don’t have to do any secondary checks against a backend system to map a user ID to a service ID.
Еще говорят, на грядущем OAuth Security Workshop 2024 как раз расскажут подробнее про проект из описанного примера. Но, увы, мероприятие нацелено на оффлайн и проходит в Риме =( Возможно, далее появятся и в онлайне какие-либо материалы с интересными примерами.

Про OAuth 2.0 Rich Authorization Requests (RAR) и Lodging Intent pattern Весь OAuth 2.0 - он про делегирование доступа, ограничивать который можно, используя понятие "scope". Но что если гранулярности, которую предоставляют scopes, недостаточно для того, чтобы запросить "fine-grained", более детально ограниченный доступ? Не так давно узнал про достаточно молодой стандарт для решения подобных проблем, RFC 9396 - OAuth 2.0 Rich Authorization Requests, также известный как RAR. Вообще для решения таких задач ранее был придуман паттерн Lodging Intent. Вкратце: сначала Client для проведения операции создает специальный ресурс (в RESTовой модели) - намерение (intent), получает ответ с идентификатором созданного ресурса, а затем уже передает его как ссылку (reference) в Authorization request. Затем Authorization server (AS) может получить данные того самого intent по его ID и запросить согласие (consent) у пользователя. После авторизации пользователем AS выпускает access token, связанный с данными intent. RAR по сути представляет собой стандартизированную альтернативу, причем без создания отдельного ресурса и передачи идентификатора "by reference". Стандарт предлагает использование дополнительного параметра "authorization_details" в Authorization request, который позволяет Client запросить подтверждение специфичного узкого доступа. Это применимо для сценариев, когда доступ запрашивается не для заранее определенных "типовых" случаев, которые можно покрыть с помощью scopes (а-ля accounts:read), а для конкретных действий, которые могут быть определены динамически. Например, запрос на предоставление доступа не к любым счетам пользователя, а к конкретному, или доступ к выполнению конкретного платежа. Если же query-параметры сильно будут бухнуть из-за передачи объемного объекта, или вообще не хочется его светить на клиентской части, любезно предлагается дополнить решение еще одним стандартом - Pushed Authorization Requests (PAR). Тогда сначала создается "заготовка" Authorization request со всеми необходимыми параметрами, а в самом запросе передается только специальный URI как ее идентификатор. То есть вся суть данного подхода в том, чтобы доступ, делегируемый стороне Client в виде access token, мог быть определен с требуемой степенью гранулярности. Таким образом поощряя создание не "универсальных" токенов для всего на свете, а ограниченных так узко, как того требуется. Чего подход с использованием scopes изначально не предполагал. При проверке полученного access token - через introspect-запрос или непосредственно, если токен представлен в виде JWT, - стороне Resource server также должны быть доступны те самые authorization_details, для которых доступ был выдан, что позволит учитывать их для принятия авторизационных решений. Применим такой подход в том же Open Banking и других сферах, где ценность доступа высока и его имеет смысл ограничивать как можно детальнее. Например, в медТех`е. Можно посмотреть еще пару статей от одного из авторов данного RFC, Torsten Lodderstedt. В первой можно прочесть изначальные размышления, нашедшие свое оформление далее в виде стандарта: - Transaction Authorization or why we need to re-think OAuth scopes - Rich OAuth 2.0 Authorization Requests А еще есть статья с похожими идеями, но с использованием Macaroons как решения: Macaroon access tokens for OAuth: Part 2 – transactional auth