Короткий ответ — конфигурация может быть «работающей», но без просмотра конкретного конфига трудно сказать, нет ли у вас пробелов. Ниже — перечень типичных рисков и улучшений для ситуации «Nginx перед Unbound (DoH/HTTP) + временные ссылки/.mobileconfig», а также пример безопасной конфигурации и рекомендации, что ещё проверить. Если пришлёте ваш nginx.conf — я укажу на конкретные проблемы.
1) Общие принципы безопасности (обязательно)
- TLS
- Разрешать только TLS 1.2+ и предпочесть TLS 1.3: ssl_protocols TLSv1.2 TLSv1.3;
- Использовать современные cipher suites (рекомендации Mozilla Modern/Intermediate). Пример для 1.2: ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:...'; включить PFS (ECDHE).
- Включить ssl_prefer_server_ciphers on (для TLS1.2), включить ssl_session_cache, настроить session ticket/ключи и их ротацию; можно выключить session tickets (ssl_session_tickets off) если не управляете ключами.
- Включить OCSP stapling: ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /path/to/fullchain.pem; и настроить резолвер для stapling.
- HSTS и другие заголовки
- Добавить Strict-Transport-Security (HSTS) с разумным max-age (например 6–12 недель при первом включении, затем больше) и includeSubDomains по необходимости.
- Security headers: X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy no-referrer (особенно если токены в URL), Content-Security-Policy по необходимости.
- Утечка токенов
- Токены в URL могут утекать через Referer / логи / прокси-кэши. Установите Referrer-Policy: no-referrer и Cache-Control: no-store, no-cache для ответов, чтобы минимизировать риск.
- Логи: если токен критичен, подумайте о том, чтобы не записывать полный URI в access_log (или маскировать токен). Nginx по умолчанию логирует $request_uri.
- Кеширование
- По умолчанию DoH-ответы не должны кэшироваться промежуточными HTTP-кэшеми. Добавьте Cache-Control: private, no-store или no-cache, Pragma: no-cache, и Vary при необходимости.
- Контроль доступа и лимиты
- Ограничьте частоту запросов: limit_req_zone + limit_req (rate limiting) и limit_conn если нужно.
- Ограничьте максимальный размер тела и таймауты: client_max_body_size, client_body_timeout, client_header_timeout, send_timeout.
- Ограничьте число одновременных соединений к upstream: limit_conn_zone/limit_conn.
- Прокси/передача заголовков
- Передавайте минимально необходимые заголовки: proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme.
- Для бинарных POST (application/dns-message) убедитесь, что Nginx не портит тело: proxy_request_buffering off (если хотите стримить), proxy_buffering off, proxy_http_version 1.1 и передача Content-Length.
- Контент .mobileconfig и HTML-страница
- Отдавайте .mobileconfig только по HTTPS, Content-Type application/x-apple-aspen-config, Content-Disposition: attachment; filename=... .
- Ограничьте срок действия ссылок на серверной стороне (не только удалением HTML): токены должны быть действительны/проверяться сервером и истекать.
- Установите заголовки безопасности (CSP, X-Frame-Options и т. п.) и Cache-Control: no-store для страниц со ссылками/токенами.
- Защита от сканирования/брутфорса
- Мониторьте и ставьте rate limit, fail2ban по логам, блокируйте подозрительные IP.
- Серверные заголовки
- server_tokens off; скрывайте версии.
- Контроль содержания ответов
- Убедитесь, что Nginx не модифицирует DNS-ответы и не декодирует бинар. Для DoH важно передавать заголовки Content-Type: application/dns-message и точно пробрасывать тело.
2) Конкретные директивы nginx, на которые обратить внимание
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_ciphers (взять из актуального Mozilla config);
- ssl_prefer_server_ciphers on;
- ssl_session_cache shared:SSL:10m;
- ssl_session_tickets off (или управлять ключами);
- ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 valid=30s;
- proxy_buffering off;
- proxy_request_buffering off; # если важно стримить бинарный body
- proxy_http_version 1.1;
- proxy_set_header Connection "";
- client_max_body_size 64k; # DoH ограничен, настройте по потребностям
- client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s;
- limit_req_zone $binary_remote_addr zone=doh_limit:10m rate=10r/s;
- add_header Strict-Transport-Security "max-age=...; includeSubDomains; preload" always;
- add_header Referrer-Policy "no-referrer" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-Frame-Options "DENY" always;
3) Минимальная примерная конфигурация для DoH (упрощённо)
(ниже — примерный фрагмент; адаптируйте пути/порт upstream)
server {
listen 443 ssl http2;
server_name dns.example.tld;
ssl_certificate /etc/letsencrypt/live/dns.example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dns.example.tld/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:...'; # взять из современного профиля
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=30s;
server_tokens off;
# security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
# rate limit
limit_req_zone $binary_remote_addr zone=doh_zone:10m rate=10r/s;
location ~ ^/([A-Za-z0-9_-]{8,})$ {
# path-based token: прокси на локальный DoH Unbound
limit_req zone=doh_zone burst=20 nodelay;
# предотвратить кэширование токена/ответа
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
proxy_pass http://127.0.0.1:8053/dns-query; # пример upstream unbound
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Connection "";
client_max_body_size 64k;
}
# static files (.mobileconfig etc.) с безопасными заголовками
location /download/ {
root /var/www/dns;
autoindex off;
# отдавать как приложение/attachment
}
}
4) Особенности, связанные с вашим рабочим процессом (временные страницы и .mobileconfig)
- Удаление HTML через 10 минут — это OK, но не полагайтесь только на удаление файла: серверная логика должна инвалидировать токен (в БД или в памяти) и не допускать повторного использования.
- Токен в ссылке: лучше передавать токен в заголовке (Authorization: Bearer TOKEN) или в теле POST, чтобы он не попал в Referer/логи. Если это неудобно (например, клиент iOS ждёт URL), обязательно делайте короткий TTL и Referrer-Policy: no-referrer.
- При генерации .mobileconfig — подпишете/проверьте содержимое и не вставляйте приватные ключи в конфиг, если это не нужно.
5) Логи и приватность
- Если вы хотите анонимность клиентов — не передавайте X-Real-IP в Unbound и не логируйте полные URI. Если хотите видеть реальные IP — передавайте и храните логи безопасно.
- Маскирование токенов в логах: можно писать отдельный формат access_log без $request_uri, или писать фильтр логов, чтобы не оставлять секреты.
6) Мониторинг и реагирование
- Настройте мониторинг числа запросов, latency, ошибок 4xx/5xx, чтобы быстро заметить злоупотребления.
- Ведите ротацию сертификатов/ключей и резервирование.
7) Что проверить у вас (список для самой быстрой проверки)
- ssl_protocols и ssl_ciphers — безопасны ли они?
- ssl_stapling включён и работает?
- Referrer-Policy установлен?
- Cache-Control для DoH-ответов — no-store?
- Таймауты и лимиты установлены?
- proxy_request_buffering/proxy_buffering используются корректно для бинарного трафика?
- Токены действительно верифицируются серверной логикой и удаляются/истекают на сервере?
- Логи не сохраняют секреты (или вы согласны с этим риском)?
Если пришлёте ваш конкретный конфиг — я просмотрю и укажу на конкретные директивы, которые стоит поправить, и дам готовый исправленный конфиг.