Запрос, приходящий в ваше приложение, несёт два вида доказательств об адресе: соседа, открывшего сетевое соединение, и HTTP-поле, которое заявляет адрес более раннего клиента. Они отвечают на разные вопросы. Сосед по сокету наблюдается, а заголовок заявляет тот, кто его написал, и ни один не должен молча заменять другой в журнале или в правиле доступа. Сравнение ниже держит их раздельно, а затем показывает единственную схему, в которой заголовок становится пригодным к использованию.

Опишите путь, прежде чем что-либо сравнивать:

Client -> approved reverse proxy -> application origin

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

Два вида доказательств об адресе

Сосед по сокету — это факт об одном TCP-соединении: что-то завершило рукопожатие с этого адреса. Это не обязательно тот человек, устройство или учётная запись, которые вам важны, потому что VPN или выход Tor могут законно быть источником, который наблюдает сервис. Но это по-прежнему единственный адрес, который проверил сам хост, и в этом вся его ценность.

Поле пересылки — это метаданные о цепочке. X-Forwarded-For, X-Real-IP и стандартизованное поле Forwarded может записать любой клиент, который достигает конечной точки, возвращающей их обратно, поэтому их достоверность полностью зависит от того, кому было разрешено их устанавливать.

Держите оба в контексте запроса под разными именами. Приложение, перезаписывающее соседа заголовком, теряет способность позже восстановить ошибку доверия, а это именно тот момент, когда она вам нужна.

Заголовок никогда не перенаправляет соединение

Запись адреса в поле пересылки не меняет источник TCP-соединения, приходящего на сервер, и нигде на пути не меняет маршрутизацию. Обычная маршрутизация VPN или исходный NAT доставляет трафик вообще без какого-либо HTTP-заголовка. Точно так же транзитный шлюз не переписывает должным образом защищённый обмен HTTPS, не изменив схему конечной точки TLS.

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

Если коллега предлагает изменить заголовок, чтобы исправить проблему маршрутизации, диагностика уже пошла не туда. Исправьте путь или исправьте конфигурацию прокси, который пишет заголовок.

Назовите прокси, которому разрешено утверждать

Два правила делают поле пересылки пригодным, и оба относятся к конфигурации, а не к коду. Край нормализует или отбрасывает всё, что приходит извне, следуя собственному документированному поведению для этих полей. Затем источник берёт заменяющий адрес из названного вами пути прокси и больше ниоткуда. Второе правило действует только пока клиенты не могут добраться до источника другим способом, поскольку источник, отвечающий и на прямые, и на проходящие через прокси запросы, не знает, какому из них он служит.

Модуль NGINX для реального IP наглядно показывает разделение. set_real_ip_from называет отправителей, чьё утверждение учитывается, и real_ip_header указывает, какое поле его несёт; первый осуществляет доверие, второй лишь говорит, куда смотреть.

set_real_ip_from 10.20.0.0/24;
real_ip_header X-Forwarded-For;
# real_ip_recursive off - take the rightmost value in the field
# real_ip_recursive on  - walk the list back past every trusted address

Одна лишь директива выбора заголовка всё ещё ничего не устанавливает насчёт того, кто отправил значение.

Безопасность транспорта должна действовать на всём пути между компонентами, которым вы доверяете. Край, который нормализует правильно, теряет эту ценность в тот момент, когда незащищённый переход ниже позволяет кому-то другому отредактировать утверждение в пути.

Сознательно обрабатывайте цепочки с несколькими переходами

Несколько прокси требуют явной модели цепочки. Не берите крайнее левое или правое значение, разделённое запятыми, только потому, что так сделал какой-то пост в блоге; выясните, какой из ваших прокси добавляет, какой заменяет, а какой сохраняет входящее поле. NGINX, например, может пройти список в обратном направлении по своему доверенному набору и остановиться на первом адресе, которым не владеет, и это правило того продукта, а не закон, которому следует каждый фреймворк. Читайте документацию своего прокси с теми же двумя вопросами: какое значение он выбирает и каким отправителям верит?

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

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

Держите оба наблюдения в журнале

Там, где диагностика разрешена, записывайте соседа соединения и нормализованный адрес клиента как два отдельных поля и добавляйте только те метаданные пересылки, которые действительно нужны цели. Затем докажите развёртывание двумя запросами: одним через прокси, который должен разумно зафиксировать оба значения, и одной попыткой напрямую к источнику, которая должна быть отклонена.

Помните, что означает эта пара. Заголовок добавляет контекст, предоставленный компонентом, которому вы решили доверять; сосед остаётся единственным адресом, который ваш хост действительно видел. Ни одно из них не является доказательством личности, и проект, который считает любое из них доказательством, ошибётся насчёт кого-то.

Похожие статьи