Определения
Идентификация - это однозначное определение субъекта (пользователя) в системе.
Аутентификация — процедура проверки подлинности, например, проверка предоставленного пользователем пароля с сохраненным в БД. Аутентификация проверяет, что клиент является тем, кем он себя называет, т.е. аутентификация позволяет осуществуить идентификацию.
Авторизация — предоставление определенному лицу или группе лиц прав на выполнение определенных действий (проверка, что пользователь авторизован, т.е. у него есть разрешение на доступ к ресурсу или действию).
Факторы аутентификации
- Фактор знания
Пользователь должен знать определенные учетные данные: пароль, PIN, ответы на вопросы безопасности - Фактор владения
Пользователь должен предоставить доказательство владения некоторой физической вещью, например, SIM-картой или аппаратным OTP-токеном (генератором одноразовых паролей) - Фактор неотъемлемости
ПОльзователь должен предоставить доказательство обладания уникальным свойством, таким как отпечаток пальца или разспознавание лица.
Методы аутентификации
- Пароли
- Cертификаты
- 2FA (одноразовые пароли, SMS-код, телефонный звонок, код в email)
- Ключи доступа (API ключи)
- Токены (JWT, SAML, …)
Основные протоколы
- HTTP аутентификация
- Basic
- Digest
- Связанные с Windows аутентификации
- Forms
- TLS (на основе сертификатов)
- SAML
- OpenID Connect (основанный на OAuth 2.0)
Самые простые и самые небезопасные
При HTTP аутентификации веб-сервер должен ответить неавторизованному клиенту
со статусом 401 Unauthorized
и заголовком WWW-Authenticate
с определением схемы и параметров аутентификации.
В этом случае веб-браузер автоматически покажет аутентификационный диалог пользователю.
Для всех последующих запросов браузер будет добавлять заголовок Authorization
с аутентификационными данными.
Сервер должен использовать этот заголовок, чтобы аутентифицировать клиента.
Basic аутентификация
Basic аутентификация действительно проста: username и пароль кодируются с использованием Base64 и : в качестве разделителя.
dXNlcm5hbWU6cGFzc3dvcmQ=
- это закодированная в base64 строка username:password
.
Этот способ аутентификации может быть использован только при HTTPs (зашифрованном) подключении, иначе промежуточные узлы могут легко получить доступ к учетным данным.
Digest аутентификация
Digest аутентификация является более безопасной альтернативой Basic аутентификации. Чтобы избежать прямой передачи пароля, сервер отправляет уникальной значение ’nonce’, затем веб-браузер шифрует пароль с помощью MD5, используя ’nonce'.
Эта аутентификация лучше Basic-аутентификации, но она все еще уязвима к man-in-the-middle (MITM) атакам. Также, эта аутентификация не позволяет пользователю хранить пароли, используя сильный хэш-функции, такие как bcrypt, т.к. пароль должен быть восстановим в исходный вид для проверки.
и Basic, и Digest аутентификации не позволяют серверу сбросить аутентификацию пользователя после периода неактивности. Единственный способ пользователю выйти из приложения - это закрыть все вкладки сайта.
Forms аутентификация (Cookie аутентификация)
Forms аутентификация не является стандартом, это распространенный способ реализации аутентификации. Приложение должно включать HTML-форму, которую пользователь заполняет и отправляет на сервер, используя POST запрос (таким образом учетные данные передаются в теле запроса, а не в его параметрах). Сервер валидирует данные формы и затем добавляет токен сессии в cookies браузера. В последующих запросах токен сессии передается серверу автоматически.
Токен сессии может быть идентификатором сессии в некотором хранилище (в памяти сервера или в key-value хранилище, таком как Redis), либо это могут быть зашифрованные или подписанные данные пользователя (этот метод описывается в секции аутентификации на основе токенов)
В этом виде аутентификации учетные данные отправляются незашифрованными, поэтому он требует HTTPs, чтобы быть безопасным.
Аутентификация на основе сертификатов
Этот вид аутентификации не очень распространен, но он намного безопаснее, чем аутентификации на основе паролей.
Аутентификация на основе сертификатов - это часть протокола TLS, она происходит в момент подключения к серверу. Сервер проверяет, что переданный клиентом сертификат подписан доверенным Certificate Authority, не просрочен и не отозван.
Этот вид аутентификации сложен в реализации и поддержке.
Двухфакторная аутентификация
Двухфакторная аутентификация (2FA) - это не стандарт, а концепт, заключающийся в том, что пользователь должен предоставить два различных фактора аутентификации, чтобы доказать свою личность. Например, приложение может комбинировать аутентификацию на основе форм и использование одноразовых паролей из SMS.
Аутентификация с помощью ключей доступа
Ключи доступа - это замена паре username/пароль. Ключ доступа обычно представляет из себя длинную строку, собранную из случайных символов. Это делает ключ доступа более безопасным, т.к. его практически невозможно подобрать перебором. Ключ также обычно проще отозвать, если он был скомпрометирован.
Для передачи ключа доступа вместе с запросом, обычно используется либо заголовок Authorization
: Authorization: Bearer <API key>
, либо произвольный заголовок, например APIKey: <API key>
.
Ключи доступа могут быть эмулированы, когда используется аутентификация на основе токенов, в этом случае ключ доступа - это токен, содержащий некоторую информацию внутри.
Аутентификация на основе токенов
JWT
JWT - JSON Web Token - это де-факто стандарт токенов аутентификации. Он состоит из трех частей:
- Заголовок - метадата о самом токене в формате JSON, например, какой алгоритм используется для подписи
- Полезная нагрузка - содержимое токена в формате JSON
- Подпись - строка, созданная алгоритмами HMAC (с секретом), RSA или ECDSA (с public/private парой ключей)
Заголовок и полезная нагрузка также кодируются с помощью Base64.
“Killer feature” этого формата является то, что токен может содержать реальные данные: роль пользователя, его разрешения, email и т.д.
Пример JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjY2MjM5MDIyLCJleHAiOjE2NjYyNDkwMjIsInJvbGUiOiJBRE1JTiIsImVtYWlsIjoic29tZUBtYWlsLmNvbSJ9
.
oflWKWY28JX_1_Kl7kBG1y6KPXChqd3uHw2f4dZwJBk
(переносы строк добавлены для красоты)
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1666239022,
"exp": 1666249022,
"role": "ADMIN",
"email": "some@mail.com"
}
Информации в токене можно доверять, т.к. он подписан цифровой подписью, поэтому кто угодно может его читать, но никто не может модифицировать - подпись не будет совпадать с содержимым. Также, не зная секрет или приватный ключ, никто, кроме сервера, не может сгенерировать токен.
JWT отлично подходит для реализации stateless backend-приложения, т.к. аутентификационные данные передаются вместе с каждым запросом, поэтому серверу не требуется хранить информацию о сессии, что упрощает масштабируемость. Распределенные системы также получают выгоду от использования JWT, потому что каждый компонент может провалидировать JWT и получить аутентификационные данные без необходимости запрашивать другой компонент, все, что для этого требуется - это либо секрет (для алгоритма HMAC), либо публичный ключ (для RSA и ECDSA).
Время истечения срока жизни токена обычно записано внутри него, поэтому сервер может легко проверить, что токен еще валиден.
Refresh tokens
Аутентификация на основе JWT часто расишряется с помощью refresh-токенов. Это специальный тип токена, который используется, чтобы обновить обычный access-токен. Он не должен использоваться ни для чего, кроме этого.
Аутентификация с помощью refresh-токенов позволяет access-токену быстро устаревать, поэтому даже если он будет скомпрометирован, злоумышленник может использовать его только в течение короткого промежутка времени. Refresh-токены, с другой стороны, должны жить долго, чтобы позволить пользователю не вводить username и пароль при каждом входе в приложение.
Refresh-токен должен быть одноразовым, чтобы при его компрометации сервер мог обнаружить использование того же refresh-токена и отозвать все пользовательские токены.
Refresh-токен должен храниться безопасно в HttpOnly cookie (без доступа JavaScript кода к нему) для домена аутентификационной системы (identity provider’а). Access-токен может быть доступен из JavaScript кода, чтобы фронтенд-приложение могло отправлять его для любого API, которое использует ту же аутентификационную систему.
SAML
Security Assertion Markup Language (SAML) - это протокол аутентификации и авторизации. Он использует XML для передачи данных. Протокол состоит из двух сторон: service provider (приложение) и identity provider (сервис, аутентифицирующий пользователей).
SAML имеет механизм верификации того, что клиент владеет токеном (JWT не обладает такой возможностью, поэтому злоумышленник может использовать JWT для того, чтобы получить доступ к приложению).
SAML предоставляет Single Sign-On (SSO), который позволяет пользователю использовать одну идентификацию на нескольких сайтах без необходимости регистироваться еще раз, если сайты использует общий identity provider.
SAML подписывает токены в XML формате похожим образом, как это делает JWT.
SAML - более сложный протокол, чем OpenID Connect, поэтому на данный момент OpenID Connect более распространен.
Подробнее прочитать о SAML можно на Хабре.
OAuth 2.0 + OpenID Connect
Эта тема будет покрыта в следующей статье или позднее здесь…