TGViewer
Искусство. Код... ИИ? Искусство. Код... ИИ? @art_code_ai · 706 subscribers
Post #95 513
🔥 BadHost (CVE-2026-48710 ): один символ в Host-заголовке == поломанная авторизация в FastAPI, LiteLLM, vLLM и ещё тысячах проектов

Ребята из X41 D-Sec в рамках аудита, спонсированного OSTIF, нашли уязвимость, которая по своему масштабу и элегантности вполне может претендовать на звание «баг года» в Python-экосистеме. Starlette — это то, на чём стоит, на минуточку, FastAPI. А на FastAPI, в свою очередь, стоит половина инфраструктуры, связанной с LLM: vLLM, LiteLLM, бесчисленные MCP-серверы, AI-агенты и внутренние сервисы. 32 000+ зависимых пакетов только на PyPI 😱

1️⃣ Суть бага

Starlette реконструирует request.url, склеивая значение HTTP-заголовка Host с путём запроса. Host при этом никак не валидируется на соответствие RFC 9112 §3.2 и RFC 3986 §3.2.2. А это значит, что символы-разделители URI (/, ?, #) в Host-заголовке при пересборке URL смещают синтаксические границы компонентов.

Проще говоря: если использовать ? в Host, то всё, что идёт после него — включая реальный путь — превращается в строку запроса с точки зрения пересобранного URL. И request.url.path начинает вводить в заблуждение.

2️⃣ Атака

Вот как это выглядит на практике. Допустим, есть middleware, проверяющий request.url.path:

@app.middleware("http")
async def auth_middleware(request: Request, call_next):
if request.url.path.startswith("/admin"):
if not is_authenticated(request):
return JSONResponse(status_code=403)
return await call_next(request)


Атакующий отправляет:

curl -i -H 'Host: foo?' http://target/admin


Что происходит? Starlette склеивает foo? + /admin и получает URL http://foo?/admin. При парсинге этого URL, /admin уходит в query string. request.url.path становится пустым или /. Middleware видит не /admin, а ерунду — и пропускает запрос. Роутер же работает по request.scope["path"] (который берётся напрямую из ASGI scope, не из Host) — и честно маршрутизирует на /admin.

И, в результате, 403 превращается в 200 🙌

3️⃣ Это критичнее, чем кажется

Проблема не ограничивается ?. Символ / в Host позволяет подменить весь компонент пути. Символ # — отрезает путь как фрагмент. Это позволяет обходить практически любые проверки, основанные на пути к эндпоинту. Любой middleware или зависимость, использующая request.url.path для принятия security-решений, уязвима. А таких — тысячи. Особенно в экосистеме AI/ML, где авторизация на inference-эндпоинтах часто реализована именно через path-based middleware.

4️⃣ Фикс

В Starlette 1.0.1 добавлена валидация Host-заголовка по грамматике в соответствии с упомянутыми выше RFC:

_HOST_RE = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9.:]+\])(?::[0-9]+)?$", re.IGNORECASE)
# ...
if host_header is not None and _HOST_RE.fullmatch(host_header):


Тривиальный патч в две (по сути) строки, на тривиальный баг, который жил в кодовой базе годами.

❗️Что делать прямо сейчас:

— Обновить Starlette до >= 1.0.1 (или FastAPI до версии, которая тянет исправленный Starlette);

— Если обновление невозможно немедленно — заменить все обращения к request.url.path на request.scope["path"];

— Проверить, что reverse прокси, за которым развернуто приложение, отбрасывает запросы с невалидным Host до того, как они доберутся до приложения. Многие делают это по умолчанию, но далеко не все конфигурации это гарантируют.

⚠ TL;DR: CVE-2026-48710 — байпасс авторизации в Starlette через невалидированный Host-заголовок. Затронуты все версии < 1.0.1. Обновляйтесь или используйте request.scope["path"] вместо request.url.path, или блокируйте некорректные по RfC Host'ы на reverse-прокси.

#уязвимости
  • 🔥 5
  • 👍 4
  • ❤ 3
  • ✍ 1
More from @art_code_ai
  1. Sep 29, 2026Объяснили с Раддой Юрьевой на Хабре, почему смешивать формальные и ИИшные подходы в задача…
  2. Sep 27, 2026🔗 Старый добрый аппсек, часть 2 Продолжаем вспоминать ключевые вехи аппсека и смежных обл…
  3. Sep 26, 2026Post #155
  4. Sep 24, 2026🔗 Старый добрый аппсек, часть 1 ...совершенно незаслуженно задвинутый на второй план ИИ-х…
  5. Sep 23, 2026Post #153
  6. Sep 21, 2026👨‍💻 Продуктивные выходные Как-то незаметно (и неожиданно) для себя с удовольствием провё…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →