Какая технология создает токен безопасности который позволяет пользователю входить в веб приложение
Разбираемся, как работает протокол OAuth 2.0 и OpenID Connect. Почему Authorization Code Grand лучший способ получения access token.
Если коротко OAuth 2.0 — протокол авторизации, позволяющий выдать одному сервису (приложению) права на доступ к ресурсам пользователя на другом сервисе. Протокол избавляет от необходимости доверять приложению логин и пароль, а также позволяет выдавать ограниченный набор прав, а не все сразу.
В этой статье рассмотрим историю возникновения и схему работы. Разберемся в чем отличие OAuth 2.0 от OpenID Connect и что такое SSO.
История возникновения OAuth
Авторизацией через социальные сети никого уже не удивишь. Нажимаешь кнопку соц сети, вжух и ты авторизовался на новом сайте. Сайт получил твоё ФИО, фотку и прочие данные. Но так было не всегда.
В «каменном веке» интернета все было проще. Вы просто давали свой логин и пароль от одного сервиса другому, чтобы тот вошел в вашу учетную запись и получил любую необходимую ему информацию.
С помощью этого стандарта вы позволяете приложению считать данные или использовать функции другого приложения от вашего имени, не сообщая ему свой пароль. Класс!
OAuth 1.0 не используется. Забудьте о нем. Используйте OAuth 2.0
Главным недостатком OAuth 1.0 была слишком большая сложность данной версии.
Начнем разбор OAuth 2.0 с ролей. Всего есть 4 роли:
- Владелец ресурса.
- Клиент.
- Сервер ресурсов.
- Авторизационный сервер.
Далее мы рассмотрим каждую из ролей.
Сервер ресурсов
На сервере ресурсов лежат ваши данные. В случае с примером выше ваши контакты Gmail это ресурс, а лежат они на серверах Gmail.
Клиент
Клиент это сервис, которому требуется доступ к вашим ресурсам. Например, Facebook требуется доступ к контактам Gmail.
Авторизационный сервер
В данном примере он принадлежит Google, так как он хранит ваши данные.
Стандарт не запрещает вам объединить Сервер ресурсов и Авторизационный сервер
Базовая схема протокола
Вернемся к нашему примеру про Facebook и Gmail. На анимации ниже, я постарался схематично изобразить, как реализовать этот пример правильно с помощью Oauth2. Стоит учитывать, что у Google есть свой сервер авторизации, который отвечает за авторизацию на любом сервисе Google. Поэтому Gmail только хранит ресурсы, но не отвечает за авторизацию.
Весь смысл в том, что Клиент должен получить каким-то образом от авторизационного сервера access_token . Способов этих четыре, о них мы поговорим дальше. Используя этот access_token Клиент может использовать его в запросах к Серверу ресурсов, чтобы получать какие-то данные.
Это строка, которая является альтернативой логину и паролю.
Особенности Access Token:
- Ограниченное время жизни.
- Его можно отозвать. Если есть подозрение, что токен украли, то мы просто запрещаем получать данные с помощью этого токена.
- Компрометация токена не значит компрометирование логина и пароля. Из токена никак не получить логин и пароль.
- По токену может быть доступна только часть данных (scope). Клиент запрашивает список разрешений, которые необходимы ему для работы, а Владелец ресурсов решает выдать такие права или нет. Например, можно выдать права только на просмотр списка контактов, тогда читать письма или редактировать контакты будет нельзя.
Помимо access_token Авторизационный сервер может выдавать также refresh_token. Это токен, который позволяет запросить новый access_token без участия Владельца ресурсов. Время жизни у такого токена намного больше access_token и его потеря гораздо серьезнее.
Не все клиенты могут гарантировать сохранность client_secret , поэтому он есть не у всех. Например, SPA без бэкенда, теоретически достать оттуда можно что угодно.
Существует возможность регистрировать клиентов динамически: RFC 7591 и RFC 7592.
Способы получения Access Token
Всего есть 4 способа:
- По авторизационному коду (Authorization Code Grand). Самый сложный и самый надежный способ.
- Неявно (Implicit)
- По логину и паролю пользователя (Resource Owner Password Credential). Только для безопасных клиентов, заслуживающих полного доверия.
- По данным клиента (Client Credentials). Получаем токен по client_id и client_secret .
Client Credentials
Начнем разбор с самой простой схемы. Этот способ придуман для межсерверного взаимодействия. У нас есть два сервера API1 и API2, и им надо как-то общаться.
2. Взамен API 1 получает access_token , с помощью которого может обратиться к API 2.
3. API 1 обращается к API 2.
4. API 2 получает запрос с access_token и обращается к авторизационному серверу для проверки действительности переданного токена (RFC 7662).
Resource Owner Password Credential
Эта схема не рекомендуется к использованию! В стандарте так и написано, если вы никакие другие схемы не можете сделать, то используйте эту.
- Владелец ресурсов передает свой логин и пароль Клиенту.
- Клиент отправляет Авторизационному серверу логин и пароль клиента, а так же свой client_id и client_secret .
3. Если предоставленные пользователем учетные данные успешно аутентифицированы, сервер авторизации вернет ответ application/json , содержащий access_token :
Authorization Code Grand
Является одним из наиболее распространённых типов разрешения, поскольку он хорошо подходит для серверных приложений, где исходный код приложения и секрет клиента не доступны посторонним.
Этот способ рекомендуемый. Используйте его, и только если это невозможно, посмотрите на другие способы. Исключением является межсерверное общение.
- Пользователь на сайте нажимает кнопку авторизации, например через Facebook.
- Происходит редирект на авторизационный сервер.
- Если активной сессии нет, то Пользователь должен залогиниться. Если активная сессия есть, то просто подтвердить авторизацию.
Пример авторизационного запроса
- response_type - Обозначает тип учетных данных, которые возвращает авторизационный сервер. Для этого способа значение должно быть code .
- redirect_uri - URL-адрес, на который авторизационный сервер будет перенаправлять браузер после авторизации пользователя. Код авторизации будет доступен в параметре code .
Так как code попадает в браузер и ничем не защищен, то это точка уязвимости. Поэтому на авторизационный код накладываются следующие ограничения:
- Код одноразовый
- Время жизни кода очень мало
5. Теперь, когда у вас есть код авторизации, вы должны обменять его на токены. Используя извлеченный код авторизации из предыдущего шага, вам нужно будет выполнить POST на URL-адрес токена.
Implicit Grant
Теперь у нас сайт без бэкенда - SPA.
Так как access_token попадает в браузер, то существует возможность его достать.
OpenID Connect
OAuth 2.0 разработан только для авторизации — для предоставления доступа к данным и функциям от одного приложения другому. OpenID Connect (OIDC) — это тонкий слой поверх OAuth 2.0, добавляющий сведения о логине и профиле пользователя, который вошел в учетную запись.
OpenID Connect позволяет реализовывать сценарии, когда единственный логин можно использовать во множестве приложений, — этот подход также известен как single sign-on (SSO)
Поток (flow) OpenID Connect выглядит так же, как и в случае OAuth 2.0. Единственная разница в том, что в первичном запросе используемый конкретный scope — openid, — а Клиент в итоге получает как access_token , так и id_token .
ID Token — это особым образом отформатированная строка символов - JSON Web Token или JWT. Сторонним наблюдателям JWT может показаться непонятной абракадаброй, однако Клиент может извлечь из JWT различную информацию, такую как ID, имя пользователя, время входа в учетную запись, срок окончания действия ID Token’а, наличие попыток вмешательства в JWT.
Эта статья посвящена детальному разбору JWT и его возможностей. Мы изучим структуру токена и построим его с нуля. Затем рассмотрим наиболее распространенные способы использования.В случае OIDC также имеется стандартный способ, с помощью которого Клиент может запросить дополнительную информацию о пользователе от Сервера авторизации, например, адрес электронной почты, используя access_token .
Заключение
Токен безопасности, или секьюрити токен (англ. Security token) – это физическое или цифровое устройство, которое обеспечивает двухфакторную аутентификацию (2FA) для пользователя, чтобы подтвердить его личность в процессе входа в систему. Обычно он используется как форма идентификации для физического доступа или как метод доступа к компьютерной системе. Токен может быть предметом или картой, которая отображает или содержит информацию о безопасности пользователя и может быть проверена системой.
Токены безопасности могут использоваться вместо традиционных паролей или в дополнение к ним. Чаще всего они используются для доступа к компьютерным сетям, но также могут обеспечивать физический доступ к зданиям и выступать в качестве электронных подписей для документов.
Как работают токены безопасности?
Токен безопасности обеспечивает аутентификацию для доступа к системе через любое устройство, генерирующее пароль. Это может быть смарт-карта, USB-ключ, мобильное устройство или RFID-карта. Устройство генерирует новый пароль при каждом использовании, поэтому токен безопасности можно использовать для входа в компьютер или виртуальную частную сеть, введя пароль, сгенерированный токеном, в приглашение.
Технология токенов безопасности основана на использовании устройства, которое генерирует случайное число, шифрует его и отправляет на сервер с информацией для аутентификации пользователя. Затем сервер отправляет обратно зашифрованный ответ, который может быть расшифрован только устройством. Устройство повторно используется для каждой аутентификации, поэтому серверу не нужно хранить информацию об имени пользователя или пароле, делая систему менее уязвимой для взлома.
Типы токенов безопасности
Для защиты различных активов и приложений используются несколько типов токенов безопасности. К ним относятся следующие:
Преимущества токена безопасности
Хотя это правда, что пароли и идентификаторы пользователей по-прежнему являются наиболее широко используемой формой аутентификации, токены безопасности являются более безопасным вариантом для защиты сетей и цифровых систем. Проблема с паролями и идентификаторами пользователей в том, что они не всегда безопасны. Злоумышленники продолжают совершенствовать методы и инструменты для взлома паролей, делая пароли уязвимыми. Данные паролей также могут быть доступны или украдены в результате утечки данных. Кроме того, пароли часто легко угадать, обычно потому, что они основаны на легко обнаруживаемой личной информации.
С другой стороны, токены безопасности используют уникальный для пользователя физический или цифровой идентификатор. Большинство форм относительно просты в использовании и удобны.
Уязвимости токенов безопасности
Хотя токены безопасности предлагают пользователям и организациям множество преимуществ, они также могут иметь недостатки. Основным недостатком физических токенов безопасности является то, что они подвержены утере или краже. Например, токен безопасности может быть утерян во время путешествия или украден неавторизованной стороной. Если токен безопасности утерян или украден, его необходимо деактивировать и заменить. Тем временем неавторизованный пользователь, владеющий токеном, может получить доступ к конфиденциальной информации и системам.
Интернет-предприниматель, специалист по SEO и SMM, E-commerce, вебмастер, блогер.
JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанный на формате JSON. Как правило, используется для передачи данных для аутентификации в клиент-серверных приложениях. Токены создаются сервером, подписываются секретным ключом и передаются клиенту, который в дальнейшем использует данный токен для подтверждения своей личности.
В этой статье разберу, что такое Access токен, Refresh токен и как с ними работать.
Для дальнейших разборов будет использован токен:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9.E4FNMef6tkjIsf7paNrWZnB88c3WyIfjONzAeEd4wF0
После того, как посетитель прошел авторизацию в нашей системе, указав свой логин и пароль, система выдает ему 2 токена: access token и refresh токен.
После чего посетитель, когда хочет получить с сервера данные, например, свой профиль, вместе с запросом он передает Access токен, как на примере выше. Сервер, получив его проверяет, что он действительный (об этом чуть ниже), вычитывает полезные данные из него (тот же user_id) и, таким образом, может идентифицировать пользователя.
Токен разделен на три основные группы: заголовок, полезные данные и сигнатура, разделенные между собой точкой.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 - это первая часть токена - есть заголовок. Она закодирована в Base64 и если её раскодировать, получим строку:
Это можно проверить прям в браузере, выполнив в консоле или js коде:
const header = atob('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'); console.log(header);typ - это наш тип токена JWT. Alg - алгоритм шифрования HMAC-SHA256. Их может быть несколько, но здесь буду говорить именно об этом алгоритме.
Вторым блоком идет eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
Это есть полезные данные, так же закодированные в Base64. После раскодирования получим:
Данные могут быть любыми. Главное, чтобы по ним можно было идентифицировать пользователя. В нашем случае - это user_id и exp - время окончания действия текущего токена.
Поскольку необходимо ограничивать токен по времени, поле exp обязательно. По нему можно проверить, актуален ли токен или нет.
Последняя часть токена - наиболее важная. У нас это E4FNMef6tkjIsf7paNrWZnB88c3WyIfjONzAeEd4wF0
Как вы уже могли заметить - первые данные передаются практически в открытом виде и раскодировать их может любой. Но шифровать их нет необходимости. Цель токена - подтвердить, что эти данные не были изменены. Вот для этих целей и выступает сигнатура. И чтобы её сгенерировать нужен приватный ключ. Ну или некая секретная фраза, которая находится только на сервере. Только с помощью этого ключа мы можем создать сигнатуру и проверить, что она была создана именно с помощью его.
Она получается примерно следующим образом:
Берем заголовок, например и кодируем его в base64, получаем ту самую часть eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Тоже самое проделываем с данными eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
После этого склеиваем их и получаем eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
Далее эти данные шифруем с помощью нашего алгоритма HMAC-SHA256 и ключа.
const header = '<"alg":"HS256","typ":"JWT">' // строка const payload = '' // строка // кодируем заголовок и данные в base64 const headerBase64 = base64urlEncode(header) const payloadBase64 = base64urlEncode(payload) // склеиваем точкой полученные строки const data = headerBase64 + '.' + payloadBase64 // кодируем алгоритмом шифрования нашим ключем шифрования const secret = '123456' const sig = HMAC-SHA256(data, secret) // и, наконец, получаем окончательный токен const jwt = data + '.' + sig"alg":"HS256","typ":"JWT">Для проверка токена необходимо проделать ту же операцию.
Берем склейку заголовок + данные, кодируем с помощью алгоритма HMAC-SHA256 и нашего приватного ключа. А далее берем сигнатуру с токена и сверяем с результатом кодирования. Если результаты совпадают - значит данные подтверждены и можно быть уверенным, что они не были подменены.
Основной токен, про который шла речь выше, обычно имеет короткий срок жизни - 15-30 минут. Больше давать не стоит.
Как только время выйдет, пользователю снова придется проходить авторизацию. Так вот чтобы этого избежать, существует Refresh токен. С помощью него можно продлить Access токен.
В действительности, Refresh токен обязательно должен быть одноразовым. Его задача - получить новую пару токенов. Как только это было сделано, предыдущий токен будет считаться недействительным. Срок жизни Refresh токена уже может быть большим - до года, а может даже и больше.
У него, обычно, нет какой-то структуры и это может быть некая случайная строка.
Генерируется Access токен и после случайная строка, например T6cjEbghMZmybUd_fhE
С нашего нового Access токена eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9.E4FNMef6tkjIsf7paNrWZnB88c3WyIfjONzAeEd4wF0 беру последние шесть знаков, получаю Ed4wF0
Склеиваю и получаю рефреш токен T6cjEbghMZmybUd_fhEEd4wF0
Это сделано для привязки Access токена к Refresh. Для получения новых токенов необходимо передать эти два токена. Делается проверка на их связку и только после валидируется Access токен. Если и второй этап прошел успешно, тогда получаем с базы данных по текущему user_id рефреш токен и сверяем с тем, что к нам пришел. Если они совпадают, тогда генерируются новые токены и в базе данных обновляется Refresh токен на новый.
Refresh токен хранится в LocalStorage и используется только когда Access токен перестал быть актуальным.
Представим ситуацию, когда у нас каким-то образом украли Access токен. Да, это уже плохо и где-то у нас брешь в безопасности. Злоумышленник в этом случае сможет им воспользоваться не более чем на 15-30 минут. После чего токен "протухнет" и перестанет быть актуальным. Ведь нужен второй токен для продления.
Если украли Refresh токен, то без Access токена (который недоступен в JS) продлить ничего нельзя и он оказывается просто бесполезным.
Самая неприятная ситуация - это когда удалось увести сразу 2 токена. В этом случае злоумышленник сможет пользоваться системой неограниченное время. Точнее когда пользователь попытается войти в систему, его не пустит, т.к. его Refresh токен уже будет неактуальным, и ему придется вводить логин и пароль. Только в этом случае злоумышленник потеряет контроль над чужой учетной записью.
В своей реализации Refresh токена использовал общую длину 24 знака. Первые 6 знаков - это дата его "протухания", следующие 12 знаков - случайно сгенерированные данные. И в конце 6 знаков - это часть Access токена последней части сигнатуры.
Дату протухания внедрил прям в токен с той целью, чтобы не хранить эту информацию где-то в другом месте, например, в базе данных.
Дата содержит год, месяц, день, час и минуты. Хранится в ASCII
Кодирование даты на Golang:
// приводим к целочисленному числу uint32. Итого 32 бита. // расчет простой: год 12 бит, месяц 4 бита, день 5 бит и т.д. Таким образом в аккурат умещаемся в 32 бита или 4 байта. date := uint32(year<<20) | uint32(month<<16) | uint32(day<<11) | uint32(hour<<6) | uint32(minute) // в цикле кодируем байты в ASCII. 1 знак это шесть бит. Итого и получаем шесть знаков даты по таблице ASCII - печатные знаки. for n := 0; n < 6; n++ < b6Bit = byte(date>>i) & 0x3F sBuilder.WriteByte(byte8bitToASCII(b6Bit)) . >Всю реализацию на Go можно изучить на Github-е
В этой статье попытался рассказать о взаимодействии двух токенов и как ими пользоваться. В сети достаточно много информации о Access токенах, однако мало, как мне показалось, информации о Refresh токенах.
Статьи из этого цикла публикуются бесплатно и доступны всем. Мы убеждены, что каждый имеет право на базовые знания о защите своих данных.
Другие статьи цикла:
Если для тебя эти материалы тривиальны — отлично! Но ты сделаешь доброе дело, отправив ссылку на них своим друзьям, знакомым и родственникам, менее подкованным в технических вопросах.
Подписываемся под данными
И людям, и программам нужно знать, что данные были созданы доверенным источником и остались неизменными. Для этого была придумана технология генерации специального хеша (подписи), который подтверждает целостность информации и достоверность ее отправителя/создателя. Для создания этой самой подписи используется схема из нескольких шагов, цель которых — защитить данные от подделки.
Придумываем коды доступа
Создание безопасных одноразовых паролей состоит из двух этапов:
- Первичная настройка — включение двухфакторной аутентификации.
- Использование пароля — непосредственный ввод кода и отправка для проверки.
В таком случае пользователь с помощью приложения, доступного на любом устройстве, сможет генерировать коды в соответствии со всеми стандартами.
Первоначальная настройка приложения заключается в обмене секретным ключом между сервером и приложением для аутентификации. Затем этот секретный ключ используется на устройстве клиента, чтобы подписать данные, которые известны и серверу, и клиенту. Этот ключ и служит главным подтверждением личности пользователя при вводе пароля на сервере.
На самом деле весь секрет — последовательность из случайных символов, которые закодированы в формате Base32. Суммарно они занимают не меньше 128 бит, а чаще и все 190 бит. Эту последовательность и видит пользователь как текст или QR-код.
Так выглядит код QR для обмена секретом
Тот же самый секрет, только текстом
Как приложение создает одноразовые коды? Все просто: приложение с помощью ключа хеширует какое-то значение, чаще всего число, берет определенную часть получившегося хеша и показывает пользователю в виде числа из шести или восьми цифр.
С самого начала для этого числа разработчики использовали простой счетчик входов. Сервер считал количество раз, которое ты заходил, например, на сайт, а приложению было известно, сколько раз ты запрашивал одноразовый пароль. Именно это значение и использовалось для создания каждого следующего одноразового кода. В современных приложениях вместо счетчика берется текущее время — и это намного удобнее для пользователя: счетчики входов часто сбивались, и их приходилось настраивать заново.
Теперь давай попробуем посчитать код для авторизации самостоятельно. Для примера представим, что мы решили прямо в Новый год опубликовать фотографию красивого фейерверка и, чтобы это сделать, нужно войти в свой аккаунт, а значит, нам не обойтись без одноразового пароля.
Возьмем время празднования Нового года в формате UNIX ( 1577811600000 ) и посчитаем порядковый номер нашего пароля: поделим на 30 секунд — 52593720 . Воспользуемся нашим секретом и вычислим хеш — по стандарту RFC 6238 это функция SHA-1:
Не забудь про аргумент -n для команды echo , чтобы в ее выводе не было ненужного перевода строки, иначе хеш будет другим.
Теперь дело за малым: нужно получить шесть цифр, которые мы и будем отправлять на сервер при авторизации. Возьмем последние четыре бита хеша — сдвиг, — это будет число a , или 10. Именно по этому сдвигу расположен наш код, который пока в виде байтов, — 03b08b12 = 61901586 . Отбросим все цифры этого числа, кроме шести последних, и получим наш новенький, готовый к использованию одноразовый код — 901586 .
Ни одно современное приложение не спрашивает пароль у пользователя постоянно, поскольку пользователей это раздражает. Именно поэтому разработчики и ученые-криптографы придумали токены, которые могут заменить собой пару логин — пароль. Перед учеными стояла задача не столько скрыть какую-то информацию, сколько создать общий стандарт для ее хранения и подтверждения ее надежности. Для всего этого была придумана технология JSON Web Token (JWT).
Как работает JWT?
Если есть данные, достоверность которых следует подтвердить, нам надо подписать их секретным ключом, используя HMAC. Для этого применяется такой же способ хеширования, что и для одноразовых паролей, только вместо шести цифр берется весь хеш целиком. Единственная разница — это сам алгоритм хеширования: в таких токенах SHA-1 считают слишком коротким и небезопасным, поэтому обычно используют SHA-256.
Главная задача JWT — подтверждение личности создателя токена и сопутствующих данных. Обычно содержимое токена — логин или другой идентификатор пользователя.
Давай попробуем создать свой токен. Продолжим нашу маленькую историю с публикацией фотографии фейерверка в соцсети: мы ввели одноразовый пароль, сервер подтвердил нашу личность и хочет выдать токен, чтобы мы смогли с его помощью открыть наше приложение.
Любой токен состоит из трех частей: заголовка со служебной информацией, данных и подписи. Так как стандартом безопасности считается SHA-256, то мы запишем его в наш заголовок.
Внутри самого токена будет храниться информация об идентификаторе аккаунта, в который мы только что вошли.
Этот хеш нам тоже надо перевести в кодировку Base64 и затем присоединить к уже имеющейся строке из заголовка и данных: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjogMTIzNDU2fQ.4Ka0ipYe4/x-s4r82xqO8i77BXLh1TM7hdsqpmkZ6Y4 — это и есть наш токен. Можно пользоваться!
Так выглядит наш токен
Подробнее про стандарт JWT можно почитать на сайте организации RFC, а про реализацию для своего любимого языка — на сайте jwt.io.
Заключение
Теперь ты знаешь, что происходит каждый день, когда ты открываешь браузер и заходишь в какой-нибудь веб-сервис. Понимая, как это работает, ты сможешь лучше защитить свои данные, а возможно, даже решишь применить какой-то из этих методов в своих разработках.
Читайте также: