Pull to refresh

Comments 2

Свой gRPC-wire вместо стандартного стека — это круто, но есть один архитектурный нюанс по безопасности.

В секции про Tsak вы пишете: «берётся самый правый хоп X-Forwarded-For, тот, который дописал доверенный прокси и который клиент подделать не может».

Если перед вашим приложением стоит цепочка балансировщиков (например, внешний Anti-DDoS -> ваш краевой Nginx), то самым правым хопом в заголовке окажется IP-адрес внешнего шлюза, а не реального атакующего. Выдав бан по этому IP при переборе, вы положите доступ вообще всем легитимным пользователям, идущим через этот узел.

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

Вы правы, и попали ровно в место, где это так. В Tsak троттл API-ключей берёт parts[^1] из XFF без списка доверенных прокси, модель однохоповая. В changelog 3.7.0 это допущение записано словами «assumes exactly one trusted proxy», а в статью оговорка не доехала. При цепочке Anti-DDoS → nginx правый хоп это адрес шлюза, и все за ним делят одну корзину. Причём это хуже, чем блокировка всех: правило «успешный вход обнуляет счётчик» в общей корзине сбрасывает счётчик и атакующему.

Ваш рецепт правильный, и он у нас уже есть, только в соседнем продукте: TrustedProxyResolverProcessor в redb.Identity делает KnownProxies/KnownNetworks, проверяет сокетный пир и идёт справа налево до первого недоверенного адреса. Tsak на эту модель переведём, единственная поправка к рецепту: нераспознаваемый хоп должен обрывать обход, а не пропускаться, иначе обход уедет в клиентскую часть заголовка. Статью поправлю, чтобы правый хоп не звучал как безусловное свойство. Спасибо, замечание нашло расхождение между двумя реализациями, а не опечатку.

Согласен, мне надо конечно внимательней надо быть.

Sign up to leave a comment.

Articles