Запит, що надходить у ваш застосунок, несе два види доказів про адресу: сусіда, який відкрив мережеве з'єднання, і поле 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, наприклад, може пройти список у зворотному напрямку щодо своєї довіреної множини й зупинитися на першій адресі, якою він не володіє, і це правило того продукту, а не закон, якого дотримується кожен фреймворк. Читайте документацію власного проксі з тими самими двома питаннями: яке значення він вибирає і яким відправникам вірить?

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

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

Тримайте обидва спостереження в журналі

Там, де діагностика дозволена, записуйте сусіда з'єднання та нормалізовану адресу клієнта як два окремі поля й додавайте лише ті метадані пересилання, які справді потрібні меті. Потім доведіть розгортання двома запитами: одним через проксі, який має розумно зафіксувати обидва значення, і однією спробою напряму до джерела, яка має бути відхилена.

Пам'ятайте, що означає ця пара. Заголовок додає контекст, наданий компонентом, якому ви вирішили довіряти; сусід залишається єдиною адресою, яку ваш хост справді бачив. Жодне з них не є доказом індивідуальної ідентичності, і проєкт, який вважає будь-яке з них доказом, помилиться щодо когось.

Схожі статті