nginx real_ip_header и X-Forwarded-For кажутся неправильными


59

Описание википедии заголовка HTTP X-Forwarded-For:

X-Forwarded-For: client1, proxy1, proxy2, ...

Документация nginx для директивы real_ip_headerгласит:

Эта директива устанавливает имя заголовка, используемого для передачи замещающего IP-адреса.
В случае X-Forwarded-For этот модуль использует последний ip в заголовке X-Forwarded-For для замены. [Акцент мой]

Эти два описания противоречат друг другу. В нашем сценарии X-Forwarded-Forзаголовок в точности соответствует описанию - «реальный» IP-адрес клиента является самой левой записью. Аналогично, поведение nginx состоит в том, чтобы использовать самое правильное значение, которое, очевидно, является лишь одним из наших прокси-серверов.

Насколько я понимаю X-Real-IP, он должен использоваться для определения фактического IP-адреса клиента, а не прокси-сервера. Я что-то упустил, или это ошибка в nginx?

И, кроме того, есть ли у кого-нибудь какие-либо предложения о том, как сделать так, чтобы X-Real-IPзаголовок отображал крайнее левое значение, как указано в определении X-Forwarded-For?

Ответы:


97

Я считаю, что ключ к решению проблем X-Forwarded-For, когда несколько IP соединены в цепочку, является недавно представленной опцией конфигурации real_ip_recursive(добавленной в nginx 1.2.1 и 1.3.0). Из документов nginx realip :

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

nginx по умолчанию захватывал последний IP-адрес в цепочке, поскольку предполагалось, что он является единственным доверенным. Но с real_ip_recursiveвключенным новым и несколькими set_real_ip_fromопциями вы можете определить несколько доверенных прокси, и он будет извлекать последний ненадежный IP.

Например, с этим конфигом:

set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

И заголовок X-Forwarded-For, в результате чего:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1

Теперь nginx выберет 123.123.123.123 в качестве IP-адреса клиента.

Что касается того, почему nginx не просто выбирает крайний левый IP-адрес и требует от вас явного определения доверенных прокси-серверов, это предотвращает простую подмену IP-адреса.

Скажем , реальный IP - адрес клиента является 123.123.123.123. Давайте также скажем, что клиент не работает, и он пытается подделать свой IP-адрес 11.11.11.11. Они отправляют запрос на сервер с таким заголовком:

X-Forwarded-For: 11.11.11.11

Поскольку обратные прокси-серверы просто добавляют IP-адреса в эту цепочку X-Forwarded-For, допустим, что в итоге это выглядит так, когда nginx доберется до него:

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1

Если вы просто захватите крайний левый адрес, это позволит клиенту легко подделать свой IP-адрес. Но в приведенном выше примере конфигурации nginx nginx будет доверять только двум последним адресам в качестве прокси. Это означает, что nginx правильно выберет 123.123.123.123в качестве IP-адреса, несмотря на то, что этот поддельный IP-адрес фактически является самым левым.


2
Большое вам спасибо за это, это действительно помогло мне. Это должен быть принятый ответ.
Хосе Ф. Романиелло

1
По умолчанию real_ip_header выглядит как X-Real-IP в соответствии с nginx.org/en/docs/http/ngx_http_realip_module.html Означает ли это, что злонамеренный пользователь может просто отправить запрос со случайным X-Real-IP, который будет использоваться как $ remote_addr в nginx (а также, возможно, передается в приложение)?
gansbrest

@gansbrest Нет, потому что set_real_ip_from ограничивает доверенные хосты.
Эль Йобо

9

Разбор X-Forwarded-Forзаголовка действительно имеет недостатки в модуле nginx real_ip.

len = r->headers_in.x_forwarded_for->value.len;
ip = r->headers_in.x_forwarded_for->value.data;

for (p = ip + len - 1; p > ip; p--) {
  if (*p == ' ' || *p == ',') {
    p++;
    len -= p - ip;
    ip = p;
    break;
  }
}

Он начинается в крайнем правом углу строки заголовка и, как только он видит пробел или запятую, он перестает смотреть и вставляет часть справа от пробела или запятой в переменной IP. Таким образом, последний адрес прокси обрабатывается как исходный адрес клиента .

Это не играет хорошо в соответствии со спецификацией; это опасность того, что это не будет изложено в болезненно очевидных терминах в RFC.

В стороне: Трудно даже найти хороший первоисточник в формате, который был первоначально определен Squid - копание их документации подтверждает порядок; крайний левый - оригинальный клиент, самый правый - самый последний. Мне очень хочется добавить [нужную цитату] на эту страницу википедии. Одно анонимное редактирование, кажется, авторитет интернета по этому вопросу.

Если возможно, можете ли вы, чтобы промежуточные прокси перестали добавлять себя в конец заголовка, оставив только реальный адрес клиента?


Спасибо за ответ, @Shane. На самом деле, при достижении nginx, X-Forwarded-Forуже существует. (это правильный IP-адрес клиента). Затем nginx сам добавляет IP-адрес нашего балансировщика нагрузки (предыдущий переход) в X-Forwarded-Forзаголовок. (предположительно добавляя то, что он видит как «удаленный адрес») Если бы он просто этого не делал, я мог бы просто использовать X-Forwarded-Forзаголовок, как и раньше. (мы недавно мигрировали в nginx)
Кирк Волл,

@Kirk Итак, когда nginx получает заголовок, это просто оригинальный адрес клиента? Но когда он обрабатывает его, он добавляется в заголовок подключающегося прокси-сервера? Это не складывается - единственный раз, когда он должен касаться этого заголовка, это когда он отправляет соединение другому прокси через proxy_pass- и даже тогда, только с proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;на месте.
Шейн Мэдден

Даже W3C понимает это неправильно : их документация утверждает, что «прокси-серверы должны добавлять IP-адрес инициатора запроса в конец списка, разделенного запятыми в поле заголовка HTTP X-Forwarded-For», он должен указывать начало .
Ян Кемп

3
@IanKemp, нет, конец правильный. Для серверной части прокси инициатором запроса (т. Е. TCP- запросом) является предыдущий прокси-сервер (если он есть). Этот предыдущий прокси, возможно, уже отправляет X-Forwarded-Forзаголовок с исходным клиентским адресом слева и, возможно, с любыми предыдущими прокси, добавленными к нему. Таким образом, текущий обслуживающий прокси-сервер добавит предыдущий прокси-сервер (= инициатор) в конец этого списка и предоставит расширенный таким образом X-Forwarded-Forзаголовок для следующего восходящего перехода. Конечно, они могли бы выбрать более очевидную формулировку.
blubberdiblub

5

X-Real-IP - это IP-адрес фактического клиента, с которым общается сервер («настоящий» клиент сервера), который в случае прокси-соединения является прокси-сервером. Вот почему X-Real-IP будет содержать последний IP-адрес в заголовке X-Forwarded-For.


1
Хорошо, но для меня это просто полезная информация. Я хочу получить исходный IP-адрес клиента - это очень важно, и в соответствии со всем, что я прочитал, назначение этих заголовков. Зачем мне знать IP-адрес наших прокси-серверов?
Кирк Волл

Если это не полезно для вас, то это не для вас. Никто не заставляет вас использовать X-Real-IP. Если вам нужен IP-адрес пользователя в приложении, попросите его проанализировать приложение X-Forwarded-For (что не всегда надежно, поскольку существуют некоторые прокси-серверы (устройства / брандмауэры для обеспечения интернет-безопасности), которые не устанавливают X-Forwarded- За). В контексте nginx X-Forwarded-For не важен, потому что он все равно не общается с этими клиентами, кроме последней записи (X-Real-IP), которая является клиентом nginx. Если вам это не нужно, не устанавливайте его, не отключайте его или просто игнорируйте: /
user558061

2
Нет, я имею в виду, почему бы X-Real-IPвозвращение IP - адрес моего собственного прокси - сервера когда - либо быть полезным?
Кирк Уолл

Отлично .. ответь мужик. Я искал эту точную информацию. Мне нужно поговорить с сервером ncat на моем прокси-сервере, поэтому мне нужно это на лету.
Югаль Джиндл
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.