OpenAPI plugin SSRF validator: resolved IP is not pinned for the connection (DNS check-time vs use-time gap)
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
Línea de trabajo
Empieza por connectors/openapi_plugin/server_url_validator.py y openapi_runner.py, especialmente por validate_server_url y run_operation alrededor de las líneas indicadas. Reproduce primero la secuencia de búsqueda DNS offline y determina después cómo se puede reutilizar la dirección validada para la solicitud, manteniendo el tratamiento del nombre de host; se considera terminado cuando la conexión no puede resolver de forma independiente una dirección rechazada por la validación.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
validate_server_url (connectors/openapi_plugin/server_url_validator.py) protects the
OpenAPI plugin against SSRF by resolving the target host and blocking
private/loopback/link-local/metadata addresses. However, the validated IP is not
reused for the actual request: openapi_runner.run_operation calls
validate_server_url(url, ...) (openapi_runner.py ~L146) with no dns_resolver, then
issues the request via httpx.AsyncClient(...).request(url=<hostname>) (~L172-186),
which re-resolves the hostname independently at connect time. A host that resolves
to a public address during validation and to a private address at connect time
(classic DNS rebinding) passes the check and is then contacted. Because
run_operation also attaches auth_callback credentials to the request, the request
that reaches the rebound address is credential-bearing.
Severity (stated honestly — this is hardening, not a high-severity SSRF)
Impact in the default configuration is low, because other layers already constrain it:
- The validator forces
httpsby default, andhttpxverifies TLS certificates
(verify=True), so a rebind to e.g.169.254.169.254fails the TLS handshake — the
request is not sent and no credential is disclosed over the default https path. The
residual over https is a blind connection attempt (TCP connect + ClientHello) to the
internal IP, not data/credential exfiltration. - Full SSRF + credential disclosure via rebinding requires an operator-configured
httpallowed_base_urlsentry, or a caller-suppliedhttp_clientwith
verify=False, or a host platform that ingests untrusted OpenAPI specs/overrides. - The feature is
@experimental.
I'm filing this as defense-in-depth: the validator is a deliberate anti-SSRF control,
and pinning the resolved IP closes the one check-time/use-time gap in it.
Reproduction (mechanism; offline)
semantic-kernel 1.44.1. Making getaddrinfo return a public IP on the 1st lookup
(validation) and a link-local IP on the 2nd (connect) shows the validator passes while
the connection target is an address it would have blocked:
import asyncio, socket
from semantic_kernel.connectors.openapi_plugin.server_url_validator import (
validate_server_url, try_categorize_non_public_address)
HOST, PUBLIC, META = "rebind.example", "93.184.216.34", "169.254.169.254"
_real, n = socket.getaddrinfo, {"i": 0}
def rebinding(host, *a, **k):
if host == HOST:
n["i"] += 1
ip = PUBLIC if n["i"] == 1 else META
return [(socket.AF_INET, socket.SOCK_STREAM, 6, "", (ip, 0))]
return _real(host, *a, **k)
socket.getaddrinfo = rebinding
async def main():
await validate_server_url(f"https://{HOST}/api/op") # 1st resolution -> public -> PASSES
connect_ip = socket.getaddrinfo(HOST, 443)[0][4][0] # 2nd -> 169.254.169.254 (what httpx uses)
print("validated public; connect IP:", connect_ip, try_categorize_non_public_address(connect_ip))
asyncio.run(main())
I did not stand up a live authoritative rebinding DNS server + real httpx connection;
this demonstrates the resolve-then-connect gap the runner relies on.
Suggested remediation
Resolve once and pin: connect to the validated IP (e.g. a custom httpx transport /
resolver that reuses the vetted address while preserving SNI/Host), or re-validate the
peer IP at connect time. Consider applying the IP check on the allowed_base_urls path
too (it currently matches on hostname strings without resolving), and re-validating
after redirects if a caller-supplied client enables follow_redirects.
- Lenguaje dominante
- C#
- Estrellas
- 28.6k
- Forks
- 4.8k
- Merge medio
- 13 h 24 min
- PR fusionados (30 d)
- 11
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/semantic-kernel
-
python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
microsoft/semantic-kernel#14491 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
microsoft/semantic-kernel#14490 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
Python: [Python] structured_outputs_transform reuses ChatHistory across calls (prompt pollution)Abiertopython triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
microsoft/semantic-kernel#14483 · 2 comentarios ·
Los mantenedores suelen responder en 2 días
-
.NET python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
microsoft/semantic-kernel#14482 · 3 comentarios ·
Los mantenedores suelen responder en 2 días
-
Python: [Python] as_agent_framework_tool drops parameter defaults (optionals become required)Abiertopython triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
microsoft/semantic-kernel#14481 ·
Los mantenedores suelen responder en 2 días
Todos los issues de microsoft/semantic-kernel
Issues similares
-
go 🏃 testing 🧪
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
valkey-io/valkey-glide#7239 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
SubtitleEdit/subtitleedit#15462 ·
Los mantenedores suelen responder en 1 día
-
:watch: Not Triaged
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
comp:instrumentation.aspnetcore
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
open-telemetry/opentelemetry-dotnet-contrib#5427 ·
Los mantenedores suelen responder en 1 día
-
[feature request] Condier making `TelemetrySpan`'s constructor and `Activity` property publicAbiertoenhancement needs-triage pkg:OpenTelemetry
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
open-telemetry/opentelemetry-dotnet#7851 · 4 comentarios ·
Los mantenedores suelen responder en 1 día