phantom-token: Модуль Phantom Token для NGINX
Установка
Вы можете установить этот модуль в любой дистрибутив на базе RHEL, включая, но не ограничиваясь:
- RedHat Enterprise Linux 7, 8, 9 и 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 и Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-phantom-token
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-phantom-token
Включите модуль, добавив следующее в начало файла /etc/nginx/nginx.conf:
load_module modules/ngx_curity_http_phantom_token_module.so;
Этот документ описывает nginx-module-phantom-token v2.0.0, выпущенный 22 мая 2025 года.
Модуль NGINX, который анализирует токены доступа в соответствии с RFC 7662, создавая "фантомный токен", который может быть передан на бэкенд API и веб-сервисы. Узнайте больше о подходе Phantom Token.
Этот модуль, когда он включен, фильтрует входящие запросы, отказывая в доступе тем, у кого нет действительного токена доступа OAuth, представленного в заголовке Authorization. Из этого заголовка извлекается access_token и анализируется с использованием настроенной конечной точки. Curity Identity Server отвечает на этот запрос в соответствии со стандартом. Для активного токена доступа тело ответа Curity Identity Server содержит JWT, который заменяет токен доступа в заголовке запроса, который NGINX передает на бэкенд. Если токен недействителен или отсутствует, запрос на бэкенд не выполняется, и вызывающему возвращается ошибка 401, несанкционированный доступ. Этот поток показан на следующей диаграмме:

Начальные вызовы приложения (веб или нативного) выполняются с использованием OpenID Connect (OIDC). Важно, что выданный токен является непрозрачным токеном доступа. Это GUID или UUID или несколько случайных байтов; в этом токене нет данных, связанных с личностью. Это фантом фактических пользовательских данных, отсюда и название -- фантомный токен. Приложение представляет токен шлюзу NGINX в соответствии со спецификацией Bearer Token Usage (т.е. RFC 6750). Этот стандарт говорит о том, что приложение должно отправить фантомный токен в заголовке запроса Authorization.
Как только сервер NGINX получает токен доступа, этот модуль вступает в действие. Используя конфигурацию, подобную приведенной ниже, этот модуль будет опрашивать запрос, находить токен и делать боковой вызов к Curity Identity Server. Этот запрос веб-сервиса будет выполнен с использованием стандарта Token Introspection (RFC 7662) с типом Accept равным application/jwt (как определено в RFC 7519). Это заставит Curity Identity Server вернуть не JSON, а просто JWT. Затем модуль передаст токен JWT на бэкенд API и микросервисы.
Если модуль также настроен для кэширования результатов вызова к Curity Identity Server (что должно быть сделано для производственных случаев), фантомный токен будет использоваться в качестве ключа кэша для соответствующего токена JWT. Это устранит необходимость в последующих вызовах к Curity Identity Server на время, пока он сообщает модулю NGINX, что может кэшировать JWT.
Кратко говоря, это очень простой API-шлюз, который работает очень быстро, высоко масштабируем и не имеет лишних функций, которые могут помешать. Весь код здесь, поэтому его легко изменить и использовать с другими OAuth-серверами!
Директивы конфигурации модуля
Версия 2.0 представила СУЩЕСТВЕННОЕ ИЗМЕНЕНИЕ для использования обновленных директив конфигурации.\ Смотрите предыдущие инструкции по конфигурации для настройки более ранних версий.
Обязательные директивы конфигурации
Директивы в этом подразделе обязательны; если какая-либо из них пропущена, модуль будет отключен.
phantom_token
Синтаксис:
phantom_tokenon|offПо умолчанию:
offКонтекст:
location
phantom_token_introspection_endpoint
Синтаксис:
phantom_token_introspection_endpointstringПо умолчанию:
—Контекст:
location
Необязательные директивы конфигурации
Следующие директивы являются необязательными и не требуют настройки.
phantom_token_realm
Синтаксис:
phantom_token_realmstringПо умолчанию:
apiКонтекст:
location
Имя защищенной области или сферы защиты, которая должна использоваться, когда клиент не предоставляет токен доступа.
Пример конфигурации:
location / {
...
phantom_token_realm "myGoodRealm";
}
phantom_token_scopes
Синтаксис:
phantom_token_scopesstringПо умолчанию:
—Контекст:
location
Список областей, разделенных пробелами, которые сервер должен сообщить клиенту как обязательные, когда он не предоставляет токен доступа.
Пример конфигурации:
location / {
...
phantom_token_scopes "scope_a scope_b scope_c";
}
phantom_token_scope
Синтаксис:
phantom_token_scopestringПо умолчанию:
—Контекст:
location
Массив областей, которые сервер должен сообщить клиенту как обязательные, когда он не предоставляет токен доступа. Если phantom_token_scopes также настроен, это значение будет иметь приоритет.
Пример конфигурации:
location / {
...
phantom_token_scope "scope_a";
phantom_token_scope "scope_b";
phantom_token_scope "scope_c";
}
Пример конфигурации
Загрузка модуля
Если модуль загружен с GitHub или скомпилирован как динамическая библиотека (по умолчанию) и не был явно скомпилирован в NGINX, его необходимо загрузить с помощью директивы load_module. Это нужно сделать в главной части конфигурации NGINX:
load_module modules/ngx_curity_http_phantom_token_module.so;
Файл может быть абсолютным или относительным путем. Если он не абсолютный, он должен быть относительным к корневому каталогу NGINX.
Параметры NGINX для конечной точки анализа
Вы также должны настроить следующие параметры NGINX для подзапроса анализа:
location curity {
internal;
proxy_pass_request_headers off;
proxy_set_header Accept "application/jwt";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
proxy_pass "https://curity.example.com/oauth/v2/oauth-introspect";
}
| Настройка анализа | Описание |
|---|---|
| internal | Запретить внешнюю доступность конечной точки анализа. |
| proxy_pass_request_headers | Установить в off, чтобы избежать использования заголовков основного запроса в подзапросе анализа. |
| Accept header | Настроить фиксированное значение application/jwt. |
| Content-Type header | Настроить фиксированное значение application/x-www-form-urlencoded. |
| Authorization header | Настроить базовую учетную запись с идентификатором клиента анализа и секретом клиента. |
Чтобы получить базовую учетную запись, соедините идентификатор клиента, двоеточие и секрет клиента, затем закодируйте их в base64. Следующая команда предоставляет пример.
echo -n "my_client_id:my_client_secret" | base64
Простая конфигурация
Следующая конфигурация может быть использована в демонстрационных или разработческих средах, где обратный прокси NGINX находится на том же хосте, что и Curity Identity Server:
server {
location /api {
phantom_token on;
phantom_token_introspection_endpoint curity;
proxy_pass https://example.com/api;
}
location curity {
internal;
proxy_pass_request_headers off;
proxy_set_header Accept "application/jwt";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
proxy_pass "https://curity.example.com/oauth/v2/oauth-introspect";
}
}
Сложная конфигурация
Следующая конфигурация более сложная, где обратный прокси NGINX находится на отдельном хосте от Curity Identity Server:
server {
server_name server1.example.com;
location /api {
phantom_token on;
phantom_token_introspection_endpoint curity;
phantom_token_realm "myGoodAPI";
phantom_token_scopes "scope_a scope_b scope_c";
proxy_pass https://example.com/api;
}
location curity {
internal;
proxy_pass_request_headers off;
proxy_set_header Accept "application/jwt";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
}
}
server {
listen 8443;
server_name server2.example.com;
location / {
proxy_pass "https://curity.example.com";
}
}
Более продвинутая конфигурация с отдельными серверами и кэшированием
Этот модуль использует встроенную директиву NGINX proxy_cache. Чтобы иметь возможность кэшировать запросы, сделанные к конечной точке анализа, кроме proxy_cache_path в контексте http и proxy_cache в контексте location, вам нужно добавить следующие 3 директивы в контекст location конечной точки анализа.
proxy_cache_methods POST;POST-запросы по умолчанию не кэшируются.proxy_cache_key $request_body;Ключ кэша связан с access_token, отправленным в оригинальном запросе. Разные запросы с одним и тем же access_token достигают одного и того же кэша.proxy_ignore_headers Set-Cookie;NGINX не будет кэшировать ответ, если заголовокSet-Cookieне игнорируется.
http {
proxy_cache_path /path/to/cache/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
server_name server1.example.com;
location /api {
phantom_token on;
phantom_token_introspection_endpoint curity;
phantom_token_scopes "scope_a scope_b scope_c";
phantom_token_realm "myGoodAPI";
proxy_pass https://example.com/api;
}
location curity {
internal;
proxy_pass_request_headers off;
proxy_set_header Accept "application/jwt";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
proxy_cache_methods POST;
proxy_cache my_cache;
proxy_cache_key $request_body;
proxy_ignore_headers Set-Cookie;
proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
}
}
server {
listen 8443;
server_name server2.example.com;
location / {
proxy_pass "https://curity.example.com";
}
}
}
Конфигурация без кэширования
Рекомендуется кэшировать результаты вызова к Curity Identity Server, чтобы избежать триггера запроса на анализ для каждого API-запроса. Если вы хотите отключить кэширование, вам следует увеличить значение по умолчанию для proxy_buffer_size, чтобы гарантировать, что модуль может читать большие JWT. Сделайте это, обновив конфигурацию запроса на анализ, как в следующем примере.
http {
server {
server_name server1.example.com;
location /api {
phantom_token on;
phantom_token_introspection_endpoint curity;
phantom_token_scopes "scope_a scope_b scope_c";
phantom_token_realm "myGoodAPI";
proxy_pass https://example.com/api;
}
location curity {
internal;
proxy_pass_request_headers off;
proxy_set_header Accept "application/jwt";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization "Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=";
proxy_ignore_headers Set-Cookie;
proxy_buffer_size 16k;
proxy_buffers 4 16k;
proxy_pass "https://server2.example.com:8443/oauth/v2/oauth-introspect";
}
}
server {
listen 8443;
server_name server2.example.com;
location / {
proxy_pass "https://curity.example.com";
}
}
}
Дополнительная информация
Для получения дополнительной информации о Curity Identity Server, его возможностях и о том, как использовать его для выдачи фантомных токенов для микросервисов, посетите curity.io. Для получения справочной информации о том, как использовать Curity Identity Server для защиты доступа к API, смотрите наши ресурсы по безопасности API.
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-phantom-token.