Какие прорехи в безопасности могут быть в конфигурации nginx?

Делаю тут впервые полностью конфигурацию на nginx без apache. Беспокоит, не пропустил ли каких тонкостей, которые могут вылиться в дыру в безопасности. спасибо всем за замечания.

Логика такая:
1. Постоянный редирект с / на index.php
2. Запрещаем любую статику кроме gif|jpg|png|js|css|ttf|woff|ico
3. Разрешаем доступ только к index.php для всех, к json.php для 1-го IP, остальные PHP файлы не должны ни открываться ни выполняться (404), для /secure доступ превентивно также блокируется (на всякий случай).
4. Разрешаем доступ к /admin только с 1-го IP, для /admin/phpmyadmin такой же доступ (но меняется CSP), а в /admin/secure доступ закрыт для всех.

P.s.: fix_pathinfo отключен

server {
    listen       443 ssl;
    server_name  site.ru;
    root         /var/www/html/;
    ssl_certificate                 /etc/nginx/cert.pem;
    ssl_certificate_key             /etc/nginx/private.key;


    index index.php;

    access_log /var/log/nginx/site-access_log;
    error_log /var/log/nginx/site-error_log;

    add_header Content-Security-Policy "default-src 'self' fonts.gstatic.com chart.googleapis.com; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' fonts.googleapis.com;";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Frame-Options "deny";
    add_header X-Content-Type-Options "nosniff";

    location = / {
        rewrite ^ $scheme://$host/index.php permanent;
    }

    location / {
        deny all;
        return 404;
    }

    location ~* \.(gif|jpg|png|js|css|ttf|woff|ico)$ {
        try_files $uri =404;
        expires 30d;
    }

    location ~* /secure/ {                                                  
        deny all;                                                              
        return 404;                                                            
    } 

    location ~* ^/index\.php$ {
        try_files $uri $uri/ =404;
        fastcgi_index index.php;
        fastcgi_pass php5-fpm-sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }

    location ~* ^/json\.php$ {
        allow 1.2.3.4;
        deny all;
        try_files $uri $uri/ =404;
        fastcgi_pass php5-fpm-sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }

    location ~* /admin/secure/ {
        deny all;
        return 404;
    }

    location ~* /admin/phpmyadmin/ {
        allow 1.2.3.4;
        deny all;
        try_files $uri $uri/ =404;
        fastcgi_index index.php;
        fastcgi_pass php5-fpm-sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
        add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval';";
    }

    location ~* /admin/ {
        allow 1.2.3.4;
        deny all;
        try_files $uri $uri/ =404;
        fastcgi_index index.php;
        fastcgi_pass php5-fpm-sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }
}
  • Вопрос задан
  • 2870 просмотров
Пригласить эксперта
Ответы на вопрос 3
1. Постоянный редирект с / на index.php

location = / {
        rewrite ^ $scheme://$host/index.php permanent;
    }

    location / {
        deny all;
        return 404;
    }
    location ~* ^/index\.php$ {
        try_files $uri $uri/ =404;
        fastcgi_index index.php;
        fastcgi_pass php5-fpm-sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include /etc/nginx/fastcgi_params;
    }



Очень странная логика, особенно использование $host и некорректное использование try_files. Про try_files почитайте тут.

$host
в порядке приоритета: имя хоста из строки запроса, или имя хоста из поля “Host” заголовка запроса, или имя сервера, соответствующего запросу


Если у вас не определен default_server и server_name site.ru; стоит первым в списке, то к вам будут прилетать все запросы с любым полем Host, в том числе пустым, что является очень опасной практикой.
Лучше использовать переменную $server_name, но все равно, если вам нужно обращения по любым url обрабатывать только через index.php, то правильно это делается так:

/NONEXISTENTFILE меняете на заранее фейковый файл который не может существовать, например /d7sdhsdhsdf8sfhgsfd8fh438dfjh

...
        error_page 404 = @cms;

        location / {
            try_files /NONEXISTENTFILE @cms;
        }

        location @cms {
                fastcgi_pass      unix:/var/lib/php5-fpm/xxxxx.sock;
                fastcgi_index    index.php;
                fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
                fastcgi_param   SCRIPT_NAME /index.php;
                include             /etc/nginx/fastcgi_params;
        }
...


2. Запрещаем любую статику кроме gif|jpg|png|js|css|ttf|woff|ico
location ~* \.(gif|jpg|png|js|css|ttf|woff|ico)$ {
        try_files $uri =404;
        expires 30d;
    }



Логичнее и правильнее будет сделать так:

try_files $uri =404; нам не понадобится, т.к. у нас есть error_page 404 = @cms; который в случае отсутствия картинки перенаправит запрос в @cms

...
        error_page 404 = @cms;

        location ~* ^.+\.(gif|jpg|png|js|css|ttf|woff|ico)$ {
                expires 30d;
                access_log off;
                log_not_found off;
        }

        location / {
            try_files /NONEXISTENTFILE @cms;
        }
...


По поводу json.php и в принципе ограничения доступа, правильнее делать это через map или geo, например так:

http {
....
        geo $my_client_ip $denied {
                default 1;
                127.0.0.1 0;
                XX.XX.XX.XX 0; # <- IP1 с которого можно заходить
                YY.YY.YY.YY 0;    # <- IP2 с которого можно заходить
        }

server {
        listen       443 ssl;
        server_name  site.ru;
        root         /var/www/html/;
...
        set $my_client_ip $remote_addr;
        if ($http_x_forwarded_client_ip ~ "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") {
                set $my_client_ip $http_x_forwarded_client_ip;
        }

        error_page 403 = @deny;

        location @deny {
                root /var/www/deny;
                rewrite ^(.*)$ /index.html break;
        }

        location ~* ^/json\.php$ {
                if ($denied) {
                        return 403;
                }
                try_files /NONEXISTENTFILE @json;
        }

        location @json {
                try_files       $uri = 404;
                fastcgi_pass    unix:/var/lib/php5-fpm/xxxxx.sock;
                fastcgi_index   index.php;
                fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include         /etc/nginx/fastcgi_params;
        }

}
}


4. Разрешаем доступ к /admin только с 1-го IP, для /admin/phpmyadmin


Не делайте так! Организуйте для phpMyAdmin отдельный поддомен с отдельным виртуальным сервером и отдельной обработкой php5-fpm через отдельный сокет.
Никогда не храните "левые" (вспомогательные) утилиты в общем каталоге web-приложения, это плохая практика. К примеру: Если в phpMyAdmin будет найдена критическая уязвимость, то даже в случае ограничения доступа к нему с определенного IP существует вероятность, что она может быть проэксплуатирована и тогда через неё поломают ваше web-приложение - оно вам нужно?

По поводу location ~* /admin/ аналогично как для json.php.

Ну и все рекомендации выше от Кирилл Несмеянов относительно hsts, ssl, ciphers, dh, ocsp stapling поддерживаю.
Ответ написан
SerafimArts
@SerafimArts
Senior Notepad Reader
1) Отсутствует hsts заголовок
2) Не указаны SSL протоколы, стоит явно прописать TLS, т.к. остальные потенциально не безопасные
3) Отсутствует перечисление ciphers (потенциально допустимы слабые и небезопасные алгоритмы)
4) Отсутствует ключик диффи
5) Используется потенциально небезопасная и устаревшая версия PHP
6) Включена (не отключена) передача заголовков сервера (т.е. информация о том, что используется nginx)
7) Отсутствуют OCSP Stapling настройки (насколько я помню - могут возникнуть проблемы с отозванными сертификатами x.509)

По поводу настройки самих урлов - хз. Самая лучшая защита и самая адекватная - это просто хранить в web-корне только то, что может быть доступно наружу, как делают все адекватные фреймы, например: https://github.com/laravel/laravel/tree/master/public
Это значит, что в вашем случае их всего два должно быть - это index и json (второй просто прикрыт по айпишнику), остальные вариант ничего не гарантируют.

Тоже самое и с phpmyadmin. Зачем он? Неужели workbench, phpstorm или ещё что с соединением сквозь ssh туннель по сертификату будет менее удобен или безопасен? Не верю.

С админкой, соглашусь, но там, кажется, всё в пределах нормы. Только, кажется, имеет смысл перенастроить каким-нибудь таким (не обязательно прям так, ибо я не проверял как работает пример) образом:
location ~ /admin/*.\.php$ {
    try_files $uri $uri/ /admin/index.php?$query_string =404;
    // ...
}

Т.е. чтобы любой запрос куда-либо отправлял на его index. Не?
Ответ написан
@Pulse
зачем вам такой монстр как phpMyAdmin?
adminer - один файл и заменяет весь этот комбайн.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы