Как настроить HSTS (HTTP Strict Transport Security) на NGINX

В этой статье описывается, как настроить NGINX и NGINX Plus для реализации политики HSTS.

Что такое HSTS?

HTTPS (HTTP, зашифрованный с помощью SSL или TLS) является неотъемлемой частью мер по защите трафика на веб-сайт, что очень затрудняет злоумышленникам перехват, изменение или подделку трафика между пользователем и веб-сайтом.

Когда пользователь вводит веб-домен вручную (предоставляя доменное имя без префикса http://или https://) или идет по простой ссылке http://, первый запрос на веб-сайт отправляется в незашифрованном виде с использованием простого HTTP.

Большинство защищенных веб‑сайтов немедленно отправляют перенаправление для обновления пользователя до HTTPS‑соединения, но злоумышленник может организовать атаку man‑in‑the‑middle (MITM), чтобы перехватить первоначальный HTTP‑запрос, и с этого момента может контролировать сеанс пользователя.

HSTS стремится устранить потенциальную уязвимость, сообщая браузеру, что доступ к домену возможен только по протоколу HTTPS. Даже если пользователь вводит или следует простой HTTP-ссылке, браузер обновляет соединение до HTTPS:

Инструменты разработчика Chrome иллюстрируют, как политика строгой транспортной безопасности HTTP (HSTS) генерирует внутреннее перенаправление для обновления HTTP до HTTPS в целях защиты трафика
Инструменты разработчика Chrome иллюстрируют, как политика HSTS
генерирует внутреннее перенаправление для обновления HTTP до HTTPS:

Chrome developer tools illustrate how an HSTS policygenerates an internal redirect to upgrade HTTP to HTTPS

Как Работает HSTS?

Политика HSTS работает путем отправки следующего заголовка HTTP-ответа с защищенных (HTTPS) веб-сайтов:

Strict-Transport-Security: max-age=31536000

Когда браузер видит этот заголовок с веб-сайта HTTPS, он “узнает”, что доступ к этому домену должен осуществляться только по протоколу HTTPS (SSL или TLS). Он кэширует эту информацию на max-age период (обычно 31 536 000 секунд, что равно примерно 1 году). Необязательный параметр includeSubDomains сообщает браузеру, что политика HSTS также применяется ко всем поддоменам текущего домена.

Strict-Transport-Security: max-age=31536000; includeSubDomains

Например, HTML-ответ для https://www.example.com может включать запрос к ресурсу https://example.com , чтобы убедиться, что HSTS установлен для всех поддоменов example.com.

Настройка HSTS в NGINX и NGINX Plus

Настройка заголовка ответа Строгой Транспортной Безопасности (STS) в NGINX и NGINX Plus относительно проста:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Параметр always гарантирует, что заголовок задан для всех ответов, включая ответы на внутренние ошибки. Более старые версии NGINX (до версии 1.7.5 или NGINX Plus R5) не поддерживают параметр always и не устанавливают заголовок для внутренних ответов на ошибки.

Правила наследования для директив add_header

Блоки конфигурации NGINX наследуют директивы add_header от своих блоков, поэтому вам нужно поместить директиву add_header в серверный блок верхнего уровня. Есть одно важное исключение: если блок содержит саму директиву add_header, он не наследует заголовки от блоков, и вам необходимо повторно объявить все директивы add_header:

server {
    listen 443 ssl;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # This 'location' block inherits the STS header
    location / {
        root /usr/share/nginx/html;
    }

    # Because this 'location' block contains another 'add_header' directive,
    # we must redeclare the STS header
    location /servlet {
        add_header X-Served-By "My Servlet Handler";
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        proxy_pass http://localhost:8080;
    }
}

Testing HTTP Strict Transport Security with Care

Once a client is presented with the HSTS policy, it caches the information for the specified max-age period. During that period, the browser refuses to access the web service over unencrypted HTTP, and refuses to grant exceptions to certificate errors (if the site previously presented a valid, trusted certificate). If you specify the includeSubDomains parameter for an HSTS policy, these restrictions also apply to all subdomains of the current domain.

It’s very difficult to back out an HSTS policy in order to remove the HTTPS version of a website or service. When you test HSTS, use a very short max-age timeout and ensure you’re comfortable with the effects and the obligation to maintain an HTTPS version of your site. When you first go live with your HSTS policy, keep max-age small and increase it only when you’re confident about doing so.

Does Every HTTPS Response Need to Have an STS Header?

The goal is to present the HSTS policy to your users as soon as possible when they begin the HTTPS session. If they don’t receive the HSTS policy during the session, they remain vulnerable to future HTTP hijacking attacks.

The browser needs to observe the STS header only once, so it’s not strictly necessary to add it to every location block and every response. However, adding it to just the home page or login page is probably not sufficient, and if you add the header only to cacheable responses, a client might not see it. Make sure you cover as much of your URL space as reasonably possible, with special attention to dynamic (non‑cacheable) content.

Running HTTP and HTTPS Versions of the Website Side by Side

Some sites run HTTP and HTTPS versions of a website within the same NGINX or NGINX Plus server, to make its content accessible through either protocol:

server {
    listen  80;
    listen  443 ssl;
    # ...
}

This is not appropriate when using HSTS because you don’t want users to access content over HTTP. Instead, you want to redirect all HTTP website accesses to use HTTPS:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;

    # Discourage deep links by using a permanent redirect to home page of HTTPS site
    return 301 https://$host;

    # Alternatively, redirect all HTTP links to the matching HTTPS page 
    # return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name www.example.com;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Strengthening HSTS

A client is protected from HTTP interception after it has seen an STS header for the relevant domain within the declared max-age period.

However, HSTS is not a perfect solution to HTTP session hijacking. Users are still vulnerable to attack if they access an HSTS‑protected website over HTTP when they have:

  • Never before visited the site
  • Recently reinstalled their operating system
  • Recently reinstalled their browser
  • Switched to a new browser
  • Switched to a new device (for example, mobile phone)
  • Deleted their browser’s cache
  • Not visited the site recently and the max-age time has passed

Source: Netcraft

To address this, Google maintains a “HSTS preload list” of web domains and subdomains that use HSTS and have submitted their names to https://hstspreload.appspot.com/. This domain list is distributed and hardcoded into major web browsers. Clients that access web domains in this list automatically use HTTPS and refuse to access the site using HTTP.

Be aware that once you set the STS header or submit your domains to the HSTS preload list, it is impossible to remove it. It’s a one‑way decision to make your domains available over HTTPS.

Read More

For more details about HSTS, check out the following resources:

If you’re considering adding the STS header to your NGINX configuration, now is also a great time to consider using other security‑focused HTTP headers, such as X-Frame-Options and X-XSS-Protection.

NGINX Plus has additional features for protecting your site from security threats and other issues, such as distributed denial‑of‑service (DDoS) attacks. To try NGINX Plus, start your free 30-day trial today or contact us to discuss your use cases.