Авторизация на сайте: cookies, токены, OAuth

Каким способом может выполняться авторизация пользователя на сайте?

Теория: аутентификация и авторизация

В веб-разработке важно различать два близких понятия. Аутентификация отвечает на вопрос «кто это?», а авторизация — «что этому разрешено делать?».

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

Отсюда возникает практическая задача: после успешного входа необходимо «привязать» дальнейшие запросы к пользователю, чтобы сервер мог:

  • Узнать пользователя.
  • Проверить срок действия входа.
  • Проверить права (роль, доступ к конкретным ресурсам).
В реальном проекте почти всегда присутствуют две части: (1) способ доставки «доказательства входа» в каждом запросе и (2) серверная проверка этого доказательства при доступе к защищённым данным.

Сессионные cookie (сервер хранит сессию)

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

Типичный сценарий:

  1. Пользователь отправляет логин/пароль на /login.
  2. Сервер проверяет данные и создаёт запись сессии на своей стороне (например, в памяти, Redis, базе данных).
  3. Сервер отправляет cookie с идентификатором сессии (например, sid=...).
  4. Браузер автоматически прикрепляет cookie к следующим запросам, а сервер по sid находит сессию и понимает, кто выполняет запрос.

Пример (упрощённо): создание сессии и установка cookie

// Псевдокод: идея одинакова во многих языках/фреймворках

POST /login
-> проверить пароль
-> sessionId = random()
-> sessions[sessionId] = { userId: 123, createdAt: now(), expiresAt: now()+... }
-> ответ:
   Set-Cookie: sid=sessionId; HttpOnly; Secure; SameSite=Lax
   200 OK

GET /profile
(браузер автоматически отправляет Cookie: sid=sessionId)
-> сервер читает sid
-> находит sessions[sid]
-> если найдено и не истекло, отдаёт страницу/JSON

Зачем серверное хранилище сессий удобно:

  • Можно быстро «отозвать» вход: достаточно удалить сессию на сервере.
  • Можно хранить дополнительные признаки (например, время входа, устройство, CSRF-токен, флаги безопасности).
  • Можно централизованно применять правила (ограничение по времени, принудительный выход).
Если злоумышленник получит значение cookie с идентификатором сессии, может возникнуть захват сессии. Поэтому важны HTTPS и корректные атрибуты cookie (например, HttpOnly, Secure, SameSite) и защита от XSS.

Токены и заголовок Authorization: Bearer

Токенный подход означает, что клиент хранит токен и отправляет его с каждым запросом к защищённому API. Часто токен передаётся в заголовке авторизации: Authorization: Bearer ....

Это описано в спецификациях семейства OAuth 2.0 (например, RFC 6750 для Bearer Token Usage), где задаётся стандартный формат передачи токена, включая пример заголовка Authorization: Bearer <token>.

Пример HTTP-запроса с Bearer-токеном

GET /api/orders HTTP/1.1
Host: example.com
Authorization: Bearer eyJhbGciOi...

Пример серверной проверки (упрощённо)

function authMiddleware(req):
  header = req.headers["authorization"]

  if header is missing:
    return 401

  if not header startsWith "Bearer ":
    return 401

  token = header after "Bearer "
  claims = verifyAndDecode(token) // подпись, срок действия, аудитория и т.п.

  if claims invalid:
    return 401

  req.userId = claims.sub
  return next()

Особенность Bearer-токена: «кто владеет токеном, тот и проходит проверку». Поэтому утечка токена обычно равна утечке доступа на время жизни токена, и требуется строго защищать каналы передачи (TLS) и место хранения.

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

OAuth 2.0 (федеративный вход)

OAuth 2.0 — это стандарт, описывающий делегирование доступа: клиентское приложение получает токен доступа к ресурсам от сервера авторизации. В сценарии «вход через провайдера» (например, через отдельный аккаунт-провайдер) OAuth 2.0 часто используется совместно со стандартами, которые добавляют «слой идентичности» (например, OpenID Connect).

Один из распространённых потоков — Authorization Code:

  1. Клиент перенаправляет браузер пользователя на endpoint авторизации.
  2. Пользователь аутентифицируется на стороне сервера авторизации и подтверждает согласие.
  3. Сервер авторизации перенаправляет браузер обратно на сайт клиента с параметром code.
  4. Сервер клиента (или доверенный backend) обменивает code на access_token (и иногда refresh_token).
  5. Далее запросы к API выполняются с предъявлением токена.

Схема (упрощённо) Authorization Code Flow

Пользователь -> Клиент (сайт) -> Сервер авторизации -> Клиент -> Ресурсный сервер

1) Клиент: redirect на /authorize
2) Сервер авторизации: вход + согласие
3) Redirect обратно: ?code=...
4) Клиент: POST /token (обмен code на access_token)
5) Клиент/приложение: запросы к API с access_token
Смысл федеративной схемы в том, что пароль пользователя не передаётся стороннему сайту: ввод учётных данных происходит только на стороне сервера авторизации.

Почему DNS, CSS/HTML и CDN-кэш — не авторизация

DNS: DNS решает задачу именования (как по доменному имени находить нужные сетевые параметры), но не содержит модели «конкретный пользователь вошёл в систему» и не обеспечивает проверку прав на уровне запросов к веб-приложению.

CSS/HTML: CSS описывает внешний вид (оформление), HTML — структуру документа. Эти технологии не предназначены для криптографической проверки личности и не дают серверу проверяемого доказательства, что запрос сделан именно конкретным пользователем.

CDN-кэш: CDN занимается доставкой и кэшированием. Кэш может хранить и повторно выдавать ответы, но это не является подтверждением личности. Наоборот, при неправильных настройках кэша возможно случайное распространение приватного контента, поэтому приватные страницы обычно помечаются так, чтобы кэширование происходило безопасно (или не происходило вовсе), а доступ контролировался cookie/токенами на сервере.

Таблица: чем отличаются подходы

ПодходЧто отправляется в каждом запросеГде хранится «состояние входа»Типичный плюсТипичный риск/сложность
Сессионные cookieCookie: sid=... (автоматически браузером)На сервере (сессия по sid)Удобно для классических сайтов, легко отзывать сессиюРиски при XSS/угоне cookie, важны атрибуты безопасности
Bearer-токенAuthorization: Bearer ...Часто «внутри» токена и/или в системе проверкиУдобно для API и разных клиентовУтечка токена обычно даёт доступ, важны срок жизни и отзыв
OAuth 2.0После получения — предъявление access tokenСервер авторизации выдаёт токены, ресурсный сервер проверяетДелегирование доступа, пароль не передаётся клиентуБольше компонентов и настроек, важны redirect URI и безопасность потока

Итого: авторизация (в смысле «вход и поддержание состояния входа») обычно реализуется через сессионные cookie или через токены (например, Authorization: Bearer ...), а для входа через внешнего провайдера применяется OAuth 2.0; DNS, CSS/HTML и CDN-кэш не предназначены для подтверждения личности пользователя.