Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Solución de problemas11 min de lectura

SSL Certificate Verify Failed: Diagnóstico de errores de cadena de confianza en Python, curl y clientes RPC Web3

Un método reproducible para rastrear SSL: CERTIFICATE_VERIFY_FAILED desde el almacén de confianza del cliente a través del handshake TLS hasta la cadena del servidor, con soluciones que no desactivan la verificación.

TL;DR

SSL: CERTIFICATE_VERIFY_FAILED significa que el cliente no pudo construir una ruta de confianza desde el certificado hoja del servidor hasta una CA raíz en su almacén de confianza local. La solución casi nunca es desactivar la verificación; es identificar cuál de los cuatro eslabones de la cadena está roto: el almacén de confianza del cliente, los certificados intermedios que envía el servidor, la coincidencia de nombre de host/SNI o el reloj. Este artículo proporciona una secuencia de diagnóstico reproducible que puede ejecutar con openssl, curl y Python, y luego aplica el mismo método a los clientes RPC Web3 que fallan contra puntos finales HTTPS.

Qué informa realmente el error

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed lo emite la biblioteca TLS (OpenSSL, BoringSSL, Secure Transport o SChannel) cuando falla la validación de ruta. El detalle final es la parte útil: unable to get local issuer certificate significa que el cliente no puede encontrar el emisor de un certificado en la cadena; certificate has expired significa que se violó una ventana de validez; hostname mismatch significa que la lista de SAN no cubre el nombre que solicitó; self-signed certificate in certificate chain significa que se presentó una raíz no confiable.

El error es una declaración del lado del cliente sobre la confianza, no una prueba de que el servidor esté roto. Un servidor puede ser perfectamente válido para los navegadores mientras falla para un proceso de Python que incluye su propio paquete de CA, o para un contenedor que no tiene instalado el paquete ca-certificates. Trate el mensaje como un puntero a uno de los cuatro eslabones: almacén de confianza local, intermedios enviados por el servidor, acuerdo de nombre/SNI y hora del sistema.

  • Almacén de confianza: ¿tiene el cliente la CA raíz que firmó la cadena?
  • Cadena: ¿envía el servidor todos los intermedios necesarios para llegar a esa raíz?
  • Identidad: ¿cubre el certificado el nombre de host y se envía correctamente el SNI?
  • Hora: ¿está el reloj del cliente dentro de la ventana notBefore/notAfter del certificado?

Suposiciones y requisitos previos

El procedimiento a continuación asume que puede ejecutar comandos en el host cliente que falla, que el TCP 443 saliente está permitido y que conoce el nombre de host exacto al que se conecta el cliente. Necesita openssl (1.1.1 o 3.x), curl y el mismo entorno de ejecución de lenguaje que produjo el error. Para el trabajo con Web3, necesita la URL RPC que usa su aplicación; manténgala en una variable de entorno como RPC_URL en lugar de codificarla directamente.

Si está eligiendo un punto final en lugar de depurar uno, la decisión concreta del proveedor corresponde a RPC Assistant; esta página se centra en el método de diagnóstico que se aplica a cualquier punto final HTTPS, incluidos los que alcanza a través del servicio API de OnFinality y las ofertas de nodos dedicados.

Modelo mental: cuatro comprobaciones independientes

La validación de ruta es una secuencia y cada paso puede fallar de forma independiente. Primero, el cliente analiza la cadena presentada. Segundo, recorre desde el certificado hoja a través de los intermedios buscando un emisor en el que ya confíe. Tercero, comprueba que el subjectAltName del certificado hoja coincida con el host solicitado. Cuarto, verifica las fechas de validez contra el reloj local. Cualquier fallo produce una variante del mismo error principal, por lo que copiar una solución de un hilo de foro a menudo no funciona: el hilo resolvió un eslabón diferente.

Esto también explica por qué pip install certifi a veces parece solucionar el problema y a veces no. Certifi solo cambia el paquete que usa un proceso de Python si ese proceso está realmente configurado para leerlo. Si el código que falla está dentro de una biblioteca de terceros, o si la variable de entorno se establece en un shell que el servicio no hereda, el cambio de paquete no tiene efecto.

Paso 1: Inspeccionar la cadena que envía el servidor

Comience en el servidor. Este comando imprime el certificado hoja y todos los intermedios que presenta el servidor, sin depender del almacén de confianza del cliente:

  • Cuente los certificados. Una cadena pública típica es el certificado hoja más uno o dos intermedios; un certificado hoja sin intermedios es una causa común de unable to get local issuer certificate.
  • Anote el emisor del último certificado. Ese nombre es la raíz en la que debe confiar el cliente.
  • Verifique notBefore y notAfter en cada certificado con openssl x509 -noout -dates.
openssl s_client -connect your-rpc-host.example:443 -servername your-rpc-host.example -showcerts </dev/null 2>/dev/null | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -noout

Paso 2: Comparar con el almacén de confianza del cliente

Ahora pida a la propia pila TLS del cliente que valide el mismo host. curl usa el almacén del sistema en la mayoría de las distribuciones de Linux y el almacén de la plataforma en macOS y Windows:

  • Si curl tiene éxito pero Python falla, el problema es el paquete de CA de Python, no el servidor.
  • Si curl falla con certificate has expired, revise el reloj del cliente antes que nada.
  • Si curl falla con unable to get local issuer certificate, es probable que el servidor no esté enviando un intermedio.
curl -vI https://your-rpc-host.example 2>&1 | grep -E "SSL certificate|subject:|issuer:|expire|TLS"

Paso 3: Reproducir dentro del entorno de ejecución que falla

La verificación predeterminada de Python utiliza el paquete compilado o configurado para el intérprete. Imprima la ruta que realmente está utilizando y luego intente una conexión mínima que aísle la capa TLS del código de la aplicación:

  • Si este fragmento falla pero curl tiene éxito, el paquete del intérprete es la línea de falla.
  • Si tiene éxito, el fallo está en la capa de cliente HTTP (requests, httpx, web3.py, ethers) o en un proxy que termina TLS.
  • Registre el texto exacto de la excepción; el sufijo después de los dos puntos es la pista procesable.
import ssl, socket, certifi

print("default verify paths:", ssl.get_default_verify_paths())
print("certifi bundle:", certifi.where())

host = "your-rpc-host.example"
ctx = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=10) as sock:
    with ctx.wrap_socket(sock, server_hostname=host) as tls:
        print("negotiated:", tls.version())
        print("peer subject:", tls.getpeercert().get("subject"))

Paso 4: Corregir el almacén de confianza sin desactivar la verificación

Cuando el paquete del intérprete está desactualizado o falta, apunte el proceso explícitamente a un paquete mantenido. Este es el mismo mecanismo que utiliza el conocido workaround SSL_CERT_FILE, pero aplicado deliberadamente en lugar de como una suposición:

  • Establezca estas variables en el entorno que realmente ejecuta el servicio, no solo en su shell interactivo.
  • En hosts Debian/Ubuntu, apt-get install -y ca-certificates y update-ca-certificates corrigen el almacén del sistema para curl y la mayoría de los entornos de ejecución.
  • En Windows, algunos entornos de ejecución usan el almacén de la plataforma y otros no; la ruta de certifi es la solución más portátil para Python.
  • Nunca incluya verify=False o rejectUnauthorized: false como resolución. Convierte un error de confianza en una exposición a man-in-the-middle no detectada.
pip install --upgrade certifi
python -c "import certifi; print(certifi.where())"
export SSL_CERT_FILE="$(python -c 'import certifi; print(certifi.where())')"
export SSL_CERT_DIR="$(dirname "$SSL_CERT_FILE")"

# verify the fix in the same shell
python -c "import urllib.request; urllib.request.urlopen('https://your-rpc-host.example', timeout=10); print('ok')"

Aplicar el método a clientes RPC Web3

Las mismas cuatro comprobaciones se aplican cuando falla una llamada JSON-RPC sobre HTTPS. Los proveedores de nodos, los balanceadores de carga y los proxies corporativos se interponen entre su cliente y la cadena, y cualquiera de ellos puede presentar una cadena en la que su entorno de ejecución no confía. Una sonda mínima separa el fallo de transporte del fallo RPC:

  • ssl_verify_result=0 significa que el handshake TLS se validó; un valor distinto de cero se asigna a un código de error de verificación de OpenSSL.
  • Si curl devuelve un resultado JSON-RPC pero su aplicación falla, el cliente HTTP de la aplicación tiene una configuración de confianza diferente.
  • Si hay un proxy en la ruta, inspeccione el certificado del proxy en lugar del de origen; el cliente solo ve la cadena del proxy.
  • Para la selección de puntos finales y la cobertura de red, consulte el directorio de redes de OnFinality y la guía de puntos finales RPC en lugar de adivinar un nombre de host.
curl -sS -X POST "$RPC_URL" \
  -H 'content-type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' \
  -w '\nhttp_code=%{http_code} tls=%{ssl_verify_result}\n'

Verificación: cómo se ve el éxito

Un caso resuelto produce evidencia consistente en todas las capas. Utilice una pequeña tabla de resultados para registrar el estado antes y después, de modo que la solución sea auditable y no anecdótica; complete los valores que observe en su propio host.

  • | Comprobación | Comando | Esperado después de la solución |
  • | Cadena del servidor | openssl s_client -showcerts | Hoja más intermedios, el último emisor es una raíz pública |
  • | Cliente del sistema | curl -vI https://host | SSL certificate verify ok |
  • | TLS del entorno de ejecución | Fragmento de Python create_default_context() | El handshake se completa, se imprime el sujeto del par |
  • | Transporte RPC | POST de curl con ssl_verify_result | 0 y un resultado JSON-RPC |
  • | Aplicación | su ruta de solicitud normal | Sin CERTIFICATE_VERIFY_FAILED en los registros |

Solución de problemas que persisten tras la corrección básica

Algunos fallos persisten después de corregir el paquete. Un contenedor construido FROM scratch o una imagen mínima puede no tener ningún almacén de CA; copie el paquete y establezca SSL_CERT_FILE en la imagen. Un servicio que se ejecuta bajo systemd o Kubernetes puede no heredar las exportaciones del shell; establezca la variable en el archivo de unidad o en la especificación del pod. Un proxy corporativo que inspecciona TLS presenta su propia raíz, que debe agregarse al paquete del cliente deliberadamente y con conocimiento del equipo de seguridad.

El desfase horario es fácil de pasar por alto en máquinas virtuales restauradas desde instantáneas y en dispositivos integrados; date -u y una sincronización NTP resuelven los errores certificate has expired que no tienen nada que ver con el certificado. Por último, si solo falla un nombre de host mientras otros del mismo proveedor tienen éxito, sospeche de SNI o de una configuración incorrecta de host virtual en el lado del servidor e infórmelo con la salida de openssl s_client.

Limitaciones y compensaciones

Fijar una ruta de paquete específica hace que el comportamiento sea reproducible pero agrega una tarea de mantenimiento: los paquetes caducan y las raíces se retiran, por lo que la ruta debe actualizarse con la imagen del entorno de ejecución. Depender del almacén del sistema es más simple pero varía según la distribución y la imagen base, por lo que el mismo código puede pasar en un portátil y fallar en CI. Desactivar la verificación nunca es una solución; elimina la única señal que distingue un proxy mal configurado de una interceptación activa.

Para el tráfico RPC de producción, la respuesta duradera es estandarizar la imagen base, incluir un paquete de CA actualizado y monitorear los fallos de handshake TLS como una métrica distinta de las tasas de error RPC. De esa manera, una regresión del almacén de confianza aparece antes de que parezca una interrupción de la cadena.

Próximos pasos

Una vez que el handshake esté limpio, confirme que el punto final que está utilizando es el que pretende y que su cobertura de red coincide con su aplicación. Revise el servicio API de OnFinality para acceso RPC gestionado, compare planes en la página de precios de RPC y utilice nodos dedicados cuando necesite rendimiento aislado y terminación TLS predecible. Si aún está seleccionando un proveedor, RPC Assistant puede devolver una recomendación concreta mientras esta página sigue siendo su referencia de diagnóstico.

  • Estandarice el paquete de CA en su imagen base y en el ejecutor de CI.
  • Agregue una sonda de inicio que realice un handshake TLS contra cada host RPC configurado.
  • Registre el resultado de verificación de OpenSSL junto con los errores RPC para que los fallos de confianza no se clasifiquen erróneamente.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar