Nginx. Фазы обработки запроса. If is Evil?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Самое страшное зло в Nginx - это if в location. Об этом написано много, в том числе на nginx.com. Процитируем кусочек:

The only 100% safe things which may be done inside if in a location context are:

- return ...;

- rewrite ... last;

Казалось бы, если использовать конструкцию вида

location / {
  if ( $condition ) {
    return 418;
  }
  ...
}

то ничего страшного не произойдет, однако, при определенном "умении", можно сломать даже то, что должно работать на 100%. Но будет ли виноват в нашей поломке if?

Предыдущие статьи

Nginx. О чем не пишут в книгах

Nginx. Фазы обработки запроса. Практика

Nginx. Трассировка. Взгляд землекопа

Предположим, что у нас был веб-сервер принимающий POST-запросы:

server {
    root /www/example_com;
    listen *:80;
    server_name  .example.com;
        
    location /index.php {
        fastcgi_pass   unix:/var/run/php-fpm.sock;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}
$ cat /www/example_com/index.php
<?php
var_dump($_REQUEST);
$ curl \
> -w "HTTP CODE: %{http_code}\n" \
> -d "key1=value1" \
> -X POST \
> "example.com/index.php" \
> 
array(1) {
  ["key1"]=>
  string(6) "value1"
}
HTTP CODE: 200

И в какой-то момент мы захотели отфильтровать запросы с пустыми телом (навеяно вопросом на форуме). Первый порыв мысли приводит конфиг к такому виду:

server {
    root /www/example_com;
    listen *:80;
    server_name  .example.com;
        
    location /index.php {
        fastcgi_pass   unix:/var/run/php-fpm.sock;
        fastcgi_param  SCRIPT_FILENAME         $document_root$fastcgi_script_name;
        include        fastcgi_params;
    
        if ( $request_body = '' ) {
          return 418;
        }
    }
}

Однако, мысль эта ошибочна, и в такой конфигурации мы всегда (как с отправленным телом так и без) получим 418-й ответ:

$ curl \
> -w "HTTP CODE: %{http_code}\n" \
> -X POST \
> "example.com/index.php" \
> 
HTTP CODE: 418
$ curl \
> -w "HTTP CODE: %{http_code}\n" \
> -d "key1=value1" \
> -X POST \
> "example.com/index.php" \
> 
HTTP CODE: 418
Как определены фазы в Nginx
typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,
    NGX_HTTP_SERVER_REWRITE_PHASE,
    NGX_HTTP_FIND_CONFIG_PHASE,
    NGX_HTTP_REWRITE_PHASE,
    NGX_HTTP_POST_REWRITE_PHASE,
    NGX_HTTP_PREACCESS_PHASE,
    NGX_HTTP_ACCESS_PHASE,
    NGX_HTTP_POST_ACCESS_PHASE,
    NGX_HTTP_PRECONTENT_PHASE,
    NGX_HTTP_CONTENT_PHASE,
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

В нашем условии используется переменная $request_body, значение которой

появляется в location’ах, обрабатываемых директивами proxy_pass, fastcgi_pass, uwsgi_pass и scgi_pass, когда тело было прочитано в буфер в памяти.

Следовательно, значение переменной устанавливается в фазе NGX_HTTP_CONTENT_PHASE, в то время как, директива if модуля ngx_http_rewrite_module исполняется в фазе NGX_HTTP_REWRITE_PHASE, то есть на момент проверки условия переменной $request_body не будет присвоено никакого значения вне зависимости от того пуст body или нет, поэтому при любых запросах ответом будет "418 I'm a teapot".

Таким образом, хоть мы и поломали гарантированно работающий вариант, if в location здесь совершенно не причем. Всему "виной" лишь порядок фаз обработки запроса.

Источник: https://habr.com/ru/post/570996/


Интересные статьи

Интересные статьи

В прошлый раз мы рассказывали о книгах, которые пригодятся специалистам по цифровой обработке сигналов, и обсуждали библиотеки на C++ для синтеза звука. Продолжим тему, но поговорим о ЯП под эти задач...
Все «за» и «против» 1С-Битрикс, какие есть альтернативы и что выгоднее знать разработчику? Читать далее
Не существует компаний, в которых не было бы работы с документами. И это — кропотливый, рутинный, но очень важный процесс. Люди хотят получать зарплату вовремя, а поставщ...
Если в вашей компании хотя бы два сотрудника, отвечающих за работу со сделками в Битрикс24, рано или поздно возникает вопрос распределения лидов между ними.
Cтатья будет полезна тем, кто думает какую выбрать CMS для интернет-магазина, сравнивает различные движки, ищет в них плюсы и минусы важные для себя.