Skip to content

security: el guardián de userinfo se salta con un '/' en la contraseña (bypass de 0954e9d) #255

Description

@luisgf

Seguimiento de #252 / #254. El guardián que entró en 0954e9d se puede
saltar
: sólo ve el userinfo cuando el @ cae dentro del netloc. Un /
(o ?, #) en la contraseña lo empuja fuera, y la credencial vuelve a
filtrarse — al DID de toda credencial emitida, o al mensaje de error.

Verificado contra 0954e9d, no es teórico.

Reproductor

from openbadgeslib.confparser import load_config, ob3_issuer_id
# config.ini:  [issuer] did = auto ; publish_url = <url>
publish_url config resultado
https://user:12345/x@host/ aceptado issuer id = did:web:user%3A12345:x%40host
https://user:secret/x@host/ aceptado ValueError: Port could not be cast to integer value as 'secret'
https://user%40host/badges/ aceptado did:web:user%25%2540host:badges

Es decir:

  1. La credencial acaba codificada dentro del DID (user%3A12345:x%40host),
    que va embebido en cada credencial OB3 firmada y entregada al receptor.
    Exactamente lo que el guardián existía para impedir.
  2. La contraseña se imprime en el mensaje de error. Ese ValueError lo
    genera urllib al evaluar parts.port, no nuestro código — así que la
    redacción que añadió security(config): reject publish_url credentials before they are published #254 (que sólo cubre los mensajes propios) no lo
    alcanza. El mensaje viaja a stdout y al campo error de --json: es el
    bug original de publish_url userinfo leaks into stdout, published files and signed credentials #252, resucitado por otra vía.

Causa raíz

urlsplit sólo puebla username / password — y sólo pone un @ en
netloc — cuando el @ está dentro de la autoridad. Con
https://user:secret/x@host/ el netloc es user:secret y el @ se va al
path. A partir de ahí:

  • si lo que precede al : es numérico, se percent-codifica dentro del DID;
  • si no, parts.port lanza el ValueError de urllib citando el valor crudo.

Las dos capas de #254 comparten el fallo, porque ambas miran lo mismo:

  • confparser.reject_url_userinfo → comprueba '@' in urlsplit(value).netloc;
  • ob3.did.did_web_from_url → comprueba '@' in parts.netloc y luego llama a
    parts.port sin protección.

Qué hace falta

  • Validar los componentes crudos, no sólo netloc: rechazar @ (y %40)
    en la autoridad y en el path. Un publish_url / base did:web no tiene
    uso legítimo para un @; la regla es deliberadamente roma y también
    rechazará un https://host/@name/ inocente, porque una vez parseado es
    indistinguible de las formas que esconden una credencial.
  • Envolver parts.port en try/except ValueError y relanzar un mensaje
    propio que no cite el valor.
  • Distinguir el caso sin // (urlsplit lee todo lo anterior al primer :
    como esquema: en un user:password@host/ sin esquema, ese "esquema" es
    el usuario, así que esa rama tampoco puede citarlo).
  • Replicar la regla en la capa de config, que es la que debe fallar primero.
  • Tests con estas tres URLs, afirmando que ni el DID devuelto ni ningún
    mensaje contienen la credencial.

Estado

El arreglo de did.py ya está escrito en el checkout de trabajo del
maintainer, sin commitear (docstring + validación de componentes crudos +
puerto protegido + ningún mensaje que cite la URL). Falta la parte de
confparser y los tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions