Bearer Token

31 августа 2026 · ~12 мин чтения

протокол авторизация http api безопасность

Bearer Token

Bearer Token — это способ доказать серверу право доступа одной строкой в HTTP-заголовке Authorization: Bearer <token>. Кто предъявил токен — тот и авторизован, без пароля и без подписи.

История

Слово «bearer» пришло из финансов. Bearer bond — облигация на предъявителя: нет имени владельца, кто держит бумагу — тот и получает выплаты. То же с bearer cheque (чек на предъявителя): не спрашивают паспорт, показал — получил. Эта модель — «владение = право» — и легла в название токена.

В веб-протоколах Bearer Token задокументирован в RFC 6750 («The OAuth 2.0 Authorization Framework: Bearer Token Usage»), опубликован в октябре 2012 года. Авторы — Майкл Джонс (Microsoft) и Дик Хардт (Salesforce, до этого Amazon). RFC 6750 — младший брат RFC 6749 (сам OAuth 2.0), они вышли одним пакетом.

До OAuth 2.0 существовал OAuth 1.0a (RFC 5849, апрель 2010). Он требовал подписывать каждый запрос криптографической подписью HMAC-SHA1 с использованием client_secret. Разработчики жаловались: реализовать правильно было сложно, любая ошибка в канонизации параметров ломала подпись. Проект OAuth 2.0 упростил радикально — «пусть будет строка, отправляй её в заголовке, а безопасность обеспечит TLS». Так появился Bearer.

Дальнейшие вехи:
- 2012 — RFC 6749/6750, «эра Bearer + JWT» начинается.
- 2015 — JWT формализован в RFC 7519. Bearer стал стандартной обёрткой для JWT.
- 2018 — RFC 8705: mTLS-bound tokens (привязка токена к клиентскому TLS-сертификату).
- 2023 — RFC 9449: DPoP (Demonstrating Proof-of-Possession). Возвращает подпись к каждому запросу — попытка вернуть безопасность OAuth 1.0, оставив простоту 2.0.

История, если коротко: маятник качнулся от «сложной подписи каждого запроса» к «простой строке», а потом обратно, потому что перехват Bearer оказался слишком лёгкой атакой.

Что это такое

Bearer Token — это транспортный формат, а не тип секрета. Он говорит только одно: «я даю тебе строку, ты её проверяешь». Что внутри строки — дело сервера:

Синтаксис заголовка описан в RFC 6750:

Authorization: Bearer <token>

Слово Bearer фиксировано, регистр не важен (bearer, BEARER работают, но канон — Bearer). После пробела идёт токен из ограниченного набора символов (A-Z a-z 0-9 - . _ ~ + / и = в конце). Это подмножество Base64URL — сделано специально, чтобы токен можно было положить куда угодно без экранирования.

Чем Bearer отличается от Basic (Authorization: Basic <base64>): Basic передаёт логин:пароль в кодировке Base64 (не шифровании). Bearer передаёт токен без имени пользователя — сервер сам понимает, кому он выдан. Basic ассоциируется с одним пользователем на всю жизнь пароля, Bearer — с одной сессией на короткий срок.

Чем Bearer отличается от API Key (кастомные заголовки X-API-Key: ...): фактически ничем принципиально — оба «строка в заголовке = доступ». Но Bearer стандартизирован, а API Key каждый сервис реализует по-своему. Многие API (Stripe, GitHub) исторически начали с API Key и постепенно переходят на Bearer.

Чем Bearer отличается от Cookie: cookie автоматически прикрепляется браузером ко всем запросам к домену. Bearer нужно явно добавить в код (в fetch/axios/curl). Cookie удобны для веб-приложений, Bearer — для API-клиентов и SPA.

Аналогии из жизни

Билет в кино. Кассир не спрашивает паспорт: показал корешок — прошёл. Кто передаст билет другу — тот и посмотрит фильм.
Где ломается: билет обычно одноразовый и на конкретный сеанс. Bearer же можно использовать сотни раз в течение TTL. Более честная аналогия — абонемент в спортзал по QR-коду: приложил телефон, повернул турникет.

Ключ от гостиничного номера. Магнитная карта не привязана к твоему лицу — приложил, дверь открылась. Кому передашь — тот и войдёт. TTL: до конца бронирования.
Где ломается: гостиничный ключ работает только с конкретной дверью. Bearer часто работает со всеми ресурсами API, к которым выдан scope — украл один токен, получил всё, что мог делать владелец.

Купон на предъявителя. «Первому пришедшему — скидка 20%». Купон не именной, кто первый — того и купон.
Где ломается: купон одноразовый. Bearer же остаётся валидным до истечения — украдешь и будешь использовать месяц, пока владелец не заметит.

Общий смысл всех аналогий: владение = право. Токен не знает, кто ты, он знает только, что кто-то однажды его получил в обмен на пароль/логин, и до конца TTL этот «кто-то» считается уполномоченным.

Как это работает

Жизненный цикл Bearer Token в типичном OAuth-приложении:

1. Аутентификация. Пользователь заходит на страницу авторизации сервиса (условно login.example.com), вводит логин и пароль. Форма отправляет данные на сервер.

2. Выдача токена. Сервер проверяет пароль. Если всё ок, генерирует токен (случайную строку или подписанный JWT) и возвращает в HTTP-ответе:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def502004a..."
}

Поле token_type: Bearer — прямое указание клиенту, что токен нужно посылать в заголовке Authorization: Bearer ....

3. Использование. Клиент (мобильное приложение, SPA, скрипт) сохраняет токен и добавляет его к каждому запросу к API:

GET /api/v1/reports HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

4. Проверка на сервере. Сервер получает запрос, парсит заголовок, извлекает токен. Дальше — два сценария:
- Opaque token: сервер идёт в БД/Redis и ищет запись с этим токеном. Если нашёл и не истёк — авторизует.
- JWT: сервер проверяет подпись публичным ключом. Если валидна и exp в будущем — авторизует, ничего не спрашивая у БД.

5. Ответ. Если токен валиден — 200 с данными. Если нет — 401 с заголовком:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="token expired"

6. Refresh. Когда access_token истёк, клиент шлёт refresh_token на специальный эндпоинт (/oauth/token с grant_type=refresh_token) и получает свежий access_token. Refresh_token живёт дольше (недели/месяцы) и хранится в более безопасном месте.

Три способа положить токен в запрос (RFC 6750 §2):

  1. HTTP-заголовок (рекомендуется): Authorization: Bearer <token>. Самый безопасный — не попадает в логи, не сохраняется в истории браузера.
  2. Form body (только для application/x-www-form-urlencoded POST): access_token=xxx в теле. Редкий вариант.
  3. URI query (не рекомендуется): ?access_token=xxx. Работает, но токен попадает в логи веб-сервера, Referer, историю браузера. Использовать только если нельзя иначе.

Обязательный TLS. Bearer передаётся в открытом виде. Без HTTPS любой сниффер на пути (публичный Wi-Fi, скомпрометированный роутер) читает токен как есть. Именно поэтому RFC 6750 §5.3 буквально требует HTTPS — и все крупные API его требуют.

Где встречается в обычной жизни

Во всех этих случаях ты не видишь ни одной строки — просто «работает». Внутри же всегда один и тот же заголовок.

Где встречается в IT и бизнесе

Классы задач, где Bearer — правильный выбор:
- API, к которому обращаются программные клиенты (не браузер с формой логина).
- Stateless-серверы, где не хочется хранить сессии в общей БД (JWT + Bearer).
- Микросервисная архитектура, где сессия должна путешествовать между сервисами.
- Публичные API с ротацией ключей.

Кто пользуется

Масштаб: только GitHub API отдаёт больше 10 миллиардов запросов в день, каждый с Bearer в заголовке. У Stripe — миллиарды платёжных операций в год, все через Bearer.

Альтернативы и конкуренты

Basic Auth (RFC 7617)
+ Простой как валенок: Authorization: Basic base64(login:password).
− Пароль передаётся при каждом запросе (в base64, что не шифрование). Утёк один запрос — утёк пароль навсегда.

Digest Auth (RFC 7616)
+ Challenge-response, пароль не передаётся напрямую.
− Сложный, устарел, редко используется. Не решает проблему MITM без TLS.

API Key в кастомном заголовке (X-API-Key: xxx)
+ Простой, привычный для многих сервисов.
− Не стандартизирован, каждый API — своя реализация. По сути тот же Bearer, но без RFC.

OAuth 1.0 (HMAC-подпись)
+ Не боится перехвата: подпись включает URL, тело, timestamp, nonce.
− Очень сложная реализация. Каждый запрос требует канонизации — источник багов.

AWS Signature Version 4 (HMAC)
+ Каждый запрос подписан, timestamp защищает от replay.
− Сложная реализация, специфичен для AWS.

mTLS (Mutual TLS)
+ Клиент имеет сертификат, аутентификация на уровне TLS-хендшейка. Токен не нужен вообще.
− Требует управления клиентскими сертификатами. Сложно для мобильных/веб-клиентов.

DPoP (RFC 9449)
+ Bearer + JWS-подпись каждого запроса. Кража токена бесполезна без ключа.
− Новый, поддержка вне OAuth-провайдеров пока слабая.

Cookie + CSRF Token
+ Автоматически прикрепляется к запросам, httpOnly защищает от XSS.
− Работает только в браузере. CSRF-токен добавляет сложности. Ровно этот сценарий Паша встретил вчера с Laravel/Смартисом — там был не Bearer, а именно cookie + XSRF-token, потому и подделка запроса не проходила.

Когда НЕ стоит использовать

Связанные понятия

Литература и источники

Где встретилось у меня

Вчера в проекте по вытягиванию аналитики из Смартиса ты через Playwright пытался обойти публичный Bearer-API, который «тихо не отдавал показы». Пришлось логиниться в UI, ловить заголовки реального XHR-запроса и повторять их. Отдельный сюжет — понимание, что там не Bearer, а сессионная кука Laravel с CSRF-токеном, поэтому подделка через fetch не проходила. Ровно классическая развилка «Bearer vs Cookie + CSRF».

Краткое резюме