Перейти к содержанию

Переменные в директивах без подстановки

Идентификатор проверки Gixy: non_expanding_variable

Многие директивы NGINX подставляют значения переменных, поэтому легко решить, что так работают все. Значения map могут содержать переменные. Значения geo и split_clients не могут: NGINX сохраняет $binary_remote_addr как строку из 19 символов $binary_remote_addr. nginx -t не выдаёт предупреждений.

Общая корзина ограничения запросов

Эта конфигурация выглядит как ограничение частоты для каждого клиента со списком исключений:

geo $limit_key {
    default   $binary_remote_addr;   # сохраняется буквально!
    127.0.0.1 "";
}
limit_req_zone $limit_key zone=dynamic:10m rate=5r/s;

Все клиенты вне списка исключений получают один и тот же ключ $binary_remote_addr и делят одну корзину. Всплеск запросов одного краулера исчерпывает лимит, и реальные посетители получают 503. Gixy сообщает об этом с уровнем HIGH и указывает директиву limit_req_zone или limit_conn_zone, использующую ключ.

Безопасная альтернатива

Пусть geo только классифицирует клиентов, а связанный map подставляет переменную. Это шаблон списка исключений из руководства NGINX по ограничению частоты:

geo $limit {
    default   1;
    127.0.0.1 0;
    10.0.0.0/8 0;
}

map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}

limit_req_zone $limit_key zone=dynamic:10m rate=5r/s;

Для split_clients используйте тот же подход: возвращайте буквальную метку и сопоставляйте её со значением переменной в map.

Шаблоны, которые никогда не подставляются

Шаблоны location, server_name, rewrite и if компилируются один раз при загрузке конфигурации, поэтому переменные в них не подставляются. Gixy сообщает о них с уровнем MEDIUM:

set $admin admin;

# Префиксный location: совпадает только с буквальным URI "/$admin/"
location /$admin/ { deny all; }

# Регулярное выражение: "$" означает конец строки, поэтому "^/$admin/"
# никогда не совпадёт, и deny молча пропускается
location ~ ^/$admin/ { deny all; }

rewrite ^/$old/(.*)$ /new/$1 permanent;   # никогда не срабатывает
if ($uri ~ $blocked) { return 403; }      # регулярное выражение - текст "$blocked"
server_name $tenant.example.com;          # совпадает с Host "$tenant.example.com"

Запишите значение явно или перенесите сравнение в map:

location ~ ^/admin/ { deny all; }

map $uri $blocked_uri {
    default         0;
    ~^/private      1;
}

Знак доллара, экранированный как \$ или внутри класса символов вроде [$], является обычным символом, и о нём не сообщается. server_name $hostname - документированное специальное имя, и оно допускается.

URI в auth_request

auth_request отправляет подзапрос на URI ровно в том виде, в каком он записан. При auth_request /auth/$host; подзапрос уходит на /auth/$host. Если какой-либо location отвечает на этот URI статусом 2xx, авторизуется каждый запрос, поэтому Gixy сообщает об этом с уровнем HIGH. Используйте фиксированный URI и передавайте данные запроса в заголовках:

location / {
    auth_request /auth;
}

location = /auth {
    internal;
    proxy_set_header X-Original-Host $host;
    proxy_pass http://auth-backend;
}

Неподставленные переменные окружения

NGINX не читает переменные окружения в конфигурации. Имя вида ${UPPER_CASE}, которое не определено через set, map или другую директиву, почти всегда является заполнителем шаблона, который не был обработан:

# Скопировано из шаблона Docker без запуска envsubst
proxy_pass http://${BACKEND_HOST}:8080;

Gixy сообщает об этом с уровнем MEDIUM. Обработайте шаблон через envsubst до запуска NGINX. Официальный образ Docker делает это для /etc/nginx/templates/*.template.

Пути без поддержки переменных

Эти директивы используют путь буквально и создают файлы или каталоги с $ в имени. Gixy сообщает о них с уровнем LOW:

Директива Буквальный аргумент
error_log путь к файлу
client_body_temp_path, proxy_temp_path, fastcgi_temp_path, uwsgi_temp_path, scgi_temp_path каталог
proxy_cache_path, fastcgi_cache_path, uwsgi_cache_path, scgi_cache_path каталог
# Создаёт файл с именем "$host.error.log"
error_log /var/log/nginx/$host.error.log;

access_log поддерживает переменные, поэтому о нём не сообщается. Переменные также подставляются в значениях map, в add_header, proxy_set_header, root, alias, try_files, proxy_pass и в ключе limit_req_zone.

Ловите такие ошибки до продакшена

GetPageSpeed Amplify запускает Gixy по расписанию на каждом сервере NGINX и оповещает о новых находках и CVE, показывая их рядом с метриками. Настроить непрерывный мониторинг.