Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

OpenAPI plugin SSRF validator: resolved IP is not pinned for the connection (DNS check-time vs use-time gap)

Abierto
#14,312 0 comentarios 0 reacciones 0 asignados Ver en GitHub

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
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
python
Área
security

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 https by default, and httpx verifies TLS certificates
    (verify=True), so a rebind to e.g. 169.254.169.254 fails 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
    http allowed_base_urls entry, or a caller-supplied http_client with
    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

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de microsoft/semantic-kernel

Todos los issues de microsoft/semantic-kernel

Issues similares

Más issues de C#

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.