Настройка NGINX. Белый список расширений файлов. Запуск только из корня.Сразу к делу.
Содержимое сайта находится в двух местах:
а) в базе данных (в нашем случае это MySQL),
б) в файлах.
О базе данных мы поговорим в другой раз, а сейчас рассмотрим файлы.
С точки зрения безопасности, все файлы сайта можно разделить на три части:
1. Статические файлы. Это файлы, которые скачиваются браузером. Готовые HTML-страницы, CSS-файлы, JS-файлы, картинки, иконки, MP3 и другие. Название этих файлов и их содержимое не является секретом, а значит этими файлами можно делиться с окружающим миром.
2. Динамические файлы. Это по сути программы (скрипты), которые не скачиваются, а запускаются браузером. У нас это только PHP-файлы. Название этих файлов также не является секретом, т.к. по названию происходит их запуск. А вот содержимое этих файлов по-хорошему должно быть секретом, чтобы злоумышленники не знали, как устроен сайт изнутри, и соответственно, не смогли подобрать ключик к нашему сайту.
3. Секретные материалы. Это файлы, доступ к которым нельзя давать даже по названию. Файлы конфигурации сервера, логи, статистика, скрипты операционной системы, служебные PHP-файлы, запускаемые только изнутри сервера, и другие. Первая опасность — это утечка информации. Вторая опасность — это запуск программы, которая не должна быть запущена, из-за чего может встать не только данный сайт, но и весь сервер.
Так вот, управлением доступа к файлам занимается веб-сервер, в нашем случае это NGINX.
Соответственно, нам нужно в настройках NGINX (для каждого сайта) описать правила доступа для каждого типа файлов.
1. Сначала опишем статику:
Код
location ~* \.(htm|html|css|js|ico|jpg|jpeg|png|gif|mp3|msi)$ {
try_files $uri $uri/ =404;
}
Здесь мы с помощью
регулярного выражения как раз перечисляем, с какими расширениями файлы будут доступны для скачивания браузером (что такое "регулярные выражения", читаем в Интернете, очень полезная вещь). Причём, доступны они будут из любых подкаталогов сайта.
2. Теперь опишем динамику:
Код
location ~* ^/(\w|-|\.)+\.php$ {
limit_req zone=max burst=5;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/site1.ru.sock;
}
Здесь мы также с помощью регулярки описываем, какое должно быть имя файла. А именно, оно должно начинаться со слеша /. Затем могут идти латинские буквы, знаки подчёркивания, дефисы и точки в любом порядке и сколько угодно, но хотя бы один такой символ. И в конце обязательно .php
То есть, это любые PHP-файлы, но обязательно из корня сайта, ибо второй слеш в этом регулярном выражении не допускается. Я сделал так специально, чтобы PHP-файлы нельзя было вызвать из подкаталогов сайта, поскольку там у меня лежат PHP-файлы только для внутреннего пользования, не предназначенные для вызова извне.
Однако, на моём сайте в одном из подкаталогов всё-таки есть PHP-файлы, вызываемые извне. И чтобы эти файлы были доступны для вызова, для них надо составить другое регулярное выражение, вот такое:
Код
^/catalog/directory/file.php$
Но блок обработки файла должен быть тот же. Не другой такой же, а один общий. То есть, у нас должно получиться два блока регулярных выражений и один блок обработки. Соответственно, из каждого блока регулярных выражений файл должен отправляться в блок обработки. Делаем сначала блок обработки:
Код
location @php {
limit_req zone=max burst=5;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/site1.ru.sock;
}
Этот блок называется именованным, т.к. имеет имя @php. По этому имени мы и сможем к нему обращаться.
Теперь делаем два блока регулярных выражений:
Код
location ~* ^/(\w|-|\.)+\.php$ {
error_page 480 = @php;
return 480;
}
location ~ ^/catalog/directory/file.php$ {
error_page 480 = @php;
return 480;
}
Как видите, мы использовали это имя блока обработки. Отправка файлов в именованный блок делается именно так.
Теперь нам нужно обработать ситуацию, когда пользователь зашёл на сайт без указания файла. То есть, просто написал в браузере "site1.ru" или "site1.ru/" (без слеша и со слешем в конце). Здесь ведь нет расширения файла .php, значит, нам нужно сделать ещё одно регулярное выражение для этих двух случаев, вот такое:
Код
(^$|^/$)
И блок у нас получится, соответственно:
Код
location ~* (^$|^/$) {
error_page 480 = @php;
return 480;
}
Который тоже будет отправлять файл в блок обработки. Но возникает закономерный вопрос: Какой файл будет отправлен в обработку, если в адресной строке не указано никакого файла? Ответ прост: Файл по умолчанию, указанный в файле
snippets/fastcgi-php.conf (в блоке обработки этот файл подключается к конфигурации). И какой же там указан файл по умолчанию? Смотрим в файл
/etc/nginx/snippets/fastcgi-php.conf и видим строку:
Код
fastcgi_index index.php
То есть, тот файл, который обычно и используется по умолчанию. Значит, если пользователь не укажет никакой файл, то будет использоваться файл
index.php, и это как раз то, что нам нужно.
В результате, динамическая часть у нас получается следующая:
Код
location ~* (^$|^/$) {
error_page 480 = @php;
return 480;
}
location ~* ^/(\w|-|\.)+\.php$ {
error_page 480 = @php;
return 480;
}
location ~ ^/catalog/directory/file.php$ {
error_page 480 = @php;
return 480;
}
location @php {
limit_req zone=max burst=5;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/site1.ru.sock;
}
3. А теперь опишем секретные файлы.
Код
location / {
deny all;
}
Что означает "Запретить всё!". А так как блок без регулярных выражений обрабатывается после блоков с регулярными выражениями, то это означает "Запретить всё остальное!". То есть, в блоках с регулярными выражениями мы описываем так называемый "белый список" разрешённых запросов к сайту (статика и динамика), а все остальные запросы тупо запрещаем. В результате, всё это мы собираем вместе, вставляем в файл конфигурации сайта
site1.ru, и у нас получается следующий файл конфигурации:
» Кликните сюда для просмотра оффтоп текста.. «
Код
# редирект с http на https и сразу без www
server {
listen 80;
listen [::]:80;
server_name site1.ru www.site1.ru;
server_tokens off; # Запрещение выдачи версии nginx в HTTP-заголовках
return 301 https://site1.ru$request_uri;
}
# редирект по https с www на без www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.site1.ru;
server_tokens off; # Запрещение выдачи версии nginx в HTTP-заголовках
ssl_certificate /etc/letsencrypt/live/site1.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site1.ru/privkey.pem;
ssl_dhparam /etc/ssl/dh/dh.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_stapling on; # Разрешение прикрепления сервером OCSP-ответов
ssl_stapling_verify on; # Разрешение проверки сервером OCSP-ответов
return 301 https://site1.ru$request_uri;
}
# основной раздел
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name site1.ru;
server_tokens off; # Запрещение выдачи версии nginx в HTTP-заголовках
ssl_certificate /etc/letsencrypt/live/site1.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site1.ru/privkey.pem;
ssl_dhparam /etc/ssl/dh/dh.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_stapling on; # Разрешение прикрепления сервером OCSP-ответов
ssl_stapling_verify on; # Разрешение проверки сервером OCSP-ответов
root /home/site1-ru/www;
# даём доступ для Let's Encrypt
location ~ /.well-known {
allow all;
}
# выдаём статические файлы
location ~* \.(htm|html|css|js|ico|jpg|jpeg|png|gif|mp3|msi)$ {
try_files $uri $uri/ =404;
}
# даём доступ переходу на сайт без указания страницы
# это не ошибка, а переход к обработчику @php
location ~* (^$|^/$) { error_page 480 = @php; return 480; }
# даём доступ для вызова php-файлов только из корня, и имя которых состоит
# из латинских символов, знаков подчёркивания, дефисов и точек
# это не ошибка, а переход к обработчику @php
location ~* ^/(\w|-|\.)+\.php$ { error_page 480 = @php; return 480; }
# даём доступ другому сайту для вызова скриптов
location ~ ^/catalog/directory/file1.php$ { error_page 480 = @php; return 480; }
location ~ ^/catalog/directory/file2.php$ { error_page 480 = @php; return 480; }
location ~ ^/catalog/directory/file3.php$ { error_page 480 = @php; return 480; }
# запрещаем всё остальное
location / {
deny all;
}
# блок обработки php-файлов
location @php {
limit_req zone=max burst=5;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/site1.ru.sock;
}
}
Который обеспечивает нам безопасность запросов к веб-серверу.