4

Потребовалось несколько часов, чтобы узнать, как правильно настроить NginX , но я все еще думаю, что мог что-то упустить. Главным образом потому, что эта отлично работающая установка вызывает больше вопросов, чем ответов. Возможно, это даже NginX NginX или что-то, представленное Debian в Version: 1.2.1-2.2+wheezy2 Package: nginx-extras , я просто не знаю.

Обратите внимание, что мой пример уже урезан до минимума (поэтому примите это как отправную точку), но он все равно должен содержать все необходимые текущие исправления безопасности.

Обратите внимание, что мне не нравится эвристика, склонная к ошибкам. Для этого он работает с /etc/php/fpm/pool.d/www.conf установленным таким образом, что php-fpm берет скрипт непосредственно из PATH_TRANSLATED вместо того, чтобы угадывать его:

php_admin_value[cgi.fix_pathinfo]=0
security.limit_extensions = .php
php_admin_flag[expose_php] = off

Вот php /home/tino/www/index.php который выводит переменные:

<?
header("content-type: text/plain");
$a = array("PATH_TRANSLATED", "SCRIPT_FILENAME", "DOCUMENT_URI", "PATH_INFO", "QUERY_STRING" );
foreach ($a as &$v)
  printf("%-15s %s\n", $v, $_SERVER[$v]);
?>

Рабочая конфигурация NginX

Обратите внимание, что /home/tino/www - это место, где живет мой тестовый скрипт. И вы, возможно, захотите заполнить все остальные параметры из /etc/nginx/fastcgi_params . Например вам нужно

fastcgi_param SCRIPT_NAME $fastcgi_script_name;

заполнить PHP_SELF .

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                try_files $fastcgi_script_name =404;

                set             $wtf              $fastcgi_path_info;
                fastcgi_param   PATH_INFO         $wtf;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

Это дает мне следующий вывод для http://example.com/index.php/a.php?b=b.php

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI    /index.php
PATH_INFO       /a.php
QUERY_STRING    b=b.php

Это именно то, что я хочу. Обратите внимание, что это работает и для менее проблемных URI, конечно.

Не работающий конфиг

Тем не менее, прямая конфигурация не работает, и я действительно озадачен, почему:

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                try_files $fastcgi_script_name =404;

                fastcgi_param   PATH_INFO         $fastcgi_path_info;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

Это то же самое, что и выше, только обходной путь $wtf отсутствует:

                set             $wtf              $fastcgi_path_info;
                fastcgi_param   PATH_INFO         $wtf;

стал

                fastcgi_param   PATH_INFO         $fastcgi_path_info;

Но это не работает, вывод для http://example.com/index.php/a.php?b=b.php теперь таков :

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI    /index.php
PATH_INFO       
QUERY_STRING    b=b.php

Как видите: PATH_INFO исчез. Это не имеет ничего общего с php-fpm , это чисто NginX !

Небезопасный конфиг работает снова

Следующая конфигурация, которая открывает большую дыру в безопасности из-за проверки отсутствующего файла, работает снова:

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                fastcgi_param   PATH_INFO         $fastcgi_path_info;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

На http://example.com/index.php/a.php?b=b.php результат немного отличается (SCRIPT_FILENAME выглядит неправильно, DOCUMENT_URI может быть правильным с другой точки зрения).

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php/a.php
DOCUMENT_URI    /index.php/a.php
PATH_INFO       /a.php
QUERY_STRING    b=b.php

Так пожалуйста

Очевидно, что try_files ломает способ $fastcgi_path_info после установки регулярного выражения с fastcgi_split_path_info .

Так, пожалуйста, кто-нибудь может сказать мне, если этот обходной путь $wtf действительно является правильным решением, или мне все еще нужно беспокоиться о дополнительных плохих побочных try_files?

Благодарю. (Если я пропустил важный бит безопасности, пожалуйста, прокомментируйте.)

2 ответа2

4

Вчера я работал над настройкой моего первого сервера Nginx с PHP-FPM, и я столкнулся с той же проблемой, что и ваш. Короче говоря, я не смог правильно и безопасно настроить Nginx со всеми правильно установленными серверными переменными PHP. Я наконец нашел правильную настройку, которой я поделюсь с вами. Тем не менее, было бы очень приятно услышать об этом эксперта Nginx. Серьезно, мы столкнулись с действительно странными проблемами, которые никогда не должны происходить.

Ваши проблемы с настройкой

Итак, в основном вам нужна работающая, безопасная установка Nginx+PHP-FPM. Под "работающим" я подразумеваю, что серверные переменные PHP установлены правильно, а под "безопасным" Nginx защищен от известных уязвимостей.

Во-первых , я смог воспроизвести вашу конфигурацию и ваши проблемы. По какой-то причине try_files что-то ломает. Я не могу сказать почему, но вы правы, когда говорите это. Замена директивы try_files следующим кодом, по-видимому, решает эту проблему:

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

РЕДАКТИРОВАТЬ: Как обсуждается ниже в комментарии, следует избегать кода выше. Более того, оригинальная директива try_files работает как положено.

Во-вторых, я заметил, что вы не включили файл fastcgi_params , который отвечает за установку всех необходимых (и обязательных) параметров FastCGI, которые будут использоваться для установки серверных переменных PHP. Я не знаю, почему вы не включили этот файл, поскольку каждая часть документации, которую я прочитал в Интернете, говорит нам, чтобы использовать этот файл. Теперь, когда файл включен, нам нужно изменить некоторые параметры, которые по умолчанию не установлены должным образом. В конце нам нужно добавить следующие директивы:

include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

Обратите внимание, что SCRIPT_FILENAME отличается от вашего. Опять же, я прочитал много документации, в которой говорится, что мы должны использовать это значение (я буду ссылаться на них ниже). Вы можете поместить директиву fastcgi_param либо непосредственно в файл fastcgi_params либо в раздел вашего location .

Тогда ничего не работает. Если я использую ту же ссылку, что и ваша, я получаю No input file specified . Просматривая файлы журнала, я получаю Unable to open primary script: /var/www/a.php (No such file or directory) . Я не знаю, почему это происходит, но все параметры fastcgi, похоже, перепутаны.

Теперь самая странная часть, если я уберу эту строку:

fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

это работает снова, но с перепутыванием серверных переменных PHP. Не только PATH_TRANSLATED не установлен, PHP_SELF и другие установлены неправильно. Это где у меня борьба долгое время. К счастью, я нашел решение, которое, кажется, соответствует тому, к чему мы стремимся.

Рабочая настройка

Если мы снова cgi.fix_pathinfo в конфигурации PHP, все будет работать. Сначала я подумал, что это плохая идея. Тем не менее, я прочитал о рисках безопасности с включенным cgi.fix_pathinfo , только чтобы узнать, что мы уже защищены от них. Если по умолчанию для security.limit_extensions установлено значение .php , мы защищены от проблем cgi.fix_pathinfo . Кроме того, проверка существования файла (условие if мы добавили ранее) защищает нас еще больше, предотвращая случай, когда FastCGI пытается интерпретировать другие файлы как PHP.

Окончательный раздел location должен выглядеть примерно так:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^((?U).+\.php)(.*)$;

    try_files $fastcgi_script_name =404;

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
}

Другие сложности

Есть одна вещь, о которой никто не говорит, даже если они все делают это по-разному. Я говорю о регулярных выражениях, используемых в директиве location и fastcgi_split_path_info . Лично я выбрал эти, так как они имеют больше смысла для меня:

location ~ ^.+\.php(/|$) { ... }
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;

Я действительно хотел бы знать преимущества и недостатки другого. Опять же, было бы очень приятно услышать об этом эксперта Nginx.

источники

1

Не уверен, почему в этом разговоре не появилась следующая ссылка: try_files & $ fastcgi_path_info

Директива try_files изменяет URI запроса на тот, который соответствует файловой системе, и последующая попытка разбить URI на $ fastcgi_script_name и $ fastcgi_path_info приводит к пустой информации о пути - поскольку в URI после try_files нет информации о пути в URI.

Я не думаю, что это следует рассматривать как ошибку, а не как функцию try_files. Это делает try_files не очень удобным для использования с fastcgi_split_path_info, но существует несколько способов его обойти, включая приведенный выше.

Упомянутый выше обходной путь - это тот, который вы упомянули в своем вопросе.

Итак, вы обнаружили, что это одна из ловушек nginx и она (с 2016 года) не будет исправлена. Ваш обходной путь вполне допустим и, на самом деле, цитируется в распространяемых фрагментах кода Debian nginx .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .