Resumen
- En TLS 1.3,
close_notifyes una afirmación autenticada y unidireccional: quien la envía no transmitirá más mensajes TLS en esa conexión. No cierra la dirección del par ni acusa recibo de una petición, respuesta o operación duradera. - Un EOF del transporte sin esa alerta conserva la posibilidad de truncamiento. Solo puede normalizarse cuando el protocolo superior tiene una regla inequívoca de completitud y la aplicación demuestra que la comprobó.
- La autoridad para reintentar necesita cuatro límites observables por separado: mensaje de aplicación, cierre TLS local y remoto, terminación del transporte y resultado persistente.
closed=trueno reemplaza esa secuencia.
Una respuesta correcta para un resultado inexistente
El servidor construyó el cuerpo de éxito antes de ejecutar el commit. Escribió los registros TLS, inició su apagado y dejó shutdown=success en el registro. Después, un conflicto de unicidad impidió que la operación quedara grabada.
La secuencia de red no era falsa. Los datos enviados precedían al close_notify auténtico del servidor. Lo que faltaba era una prueba de otro sistema: identificador de commit, confirmación durable o consulta de resultado. El cliente había recibido el final del discurso TLS del servidor, no el final del negocio.
Un diagnóstico basado solo en la última línea del socket convierte orden en significado. TLS puede ordenar bytes y autenticar una alerta. No conoce la base de datos, el estado de una cola ni la decisión de un servicio posterior.
Dos direcciones, dos decisiones
close_notify termina una dirección de escritura. El emisor declara que no mandará más mensajes TLS, y cualquier dato posterior debe ignorarse. La lectura puede continuar porque el par quizá todavía tenga datos pendientes antes de cerrar su propio sentido.
Cada extremo debe emitir la alerta antes de cerrar su lado de escritura, salvo que ya haya enviado una alerta de error. TLS 1.3 eliminó la obligación antigua de responder de inmediato y descartar escrituras pendientes; esa conducta podía cortar la salida restante del receptor.
TCP también permite medio cierre, pero su FIN es otra evidencia. Termina un flujo ordenado de bytes y no es una alerta TLS autenticada. Tampoco identifica el último registro protegido completo ni la semántica de la aplicación.
EOF conserva incertidumbre
Si el transporte termina antes de close_notify, el receptor no puede saber si recibió todo lo que el emisor pretendía mandar. Puede deberse a una caída, un timeout de proxy o una implementación incompatible. Ninguna de esas causas convierte el EOF en prueba de ataque; todas dejan ausente el límite criptográfico más fuerte.
La telemetría debe conservar también la diferencia entre alerta fatal, RST, timeout, EOF inesperado, alerta de cierre recibida e intento local de cierre. Unificarlo todo bajo “desconectado” impide decidir qué se puede usar y qué debe reconciliarse.
IANA asigna a close_notify el valor de alerta 0. El registro establece el punto de código y su referencia. No demuestra qué trayectoria siguió una conexión concreta ni si el mensaje anterior estaba completo.
Un mensaje termina por sus propias reglas
TLS trata el contenido protegido como opaco. Que varios registros lleguen antes de la alerta prueba su orden, no que formen una respuesta entera, contengan el delimitador final o expresen un commit real.
La aplicación necesita un final propio: longitud declarada, fragmento terminal, FIN de stream, código final, acuse, identificador de petición o entrada duradera. El valor depende del protocolo. Un mensaje completo puede reportar un fallo; una operación exitosa puede perder su respuesta.
El buffering crea más estados. Que una API acepte una escritura puede significar solo que los bytes entraron en TLS o en un BIO. En una conexión no bloqueante, el proceso puede registrar el intento de alerta antes de que esta salga. Ningún estado de buffer confirma por sí mismo una acción externa.
Los retornos de OpenSSL cuentan una secuencia
El primer SSL_shutdown() suele devolver 0 cuando ya se envió el close_notify local pero todavía no llegó el del par. Es una transición válida, no un error y tampoco un cierre en ambos sentidos. Un 1 indica que las dos alertas fueron enviadas y recibidas.
La primera etapa termina la escritura TLS, mantiene disponible la lectura y deja abierto TCP. OpenSSL recomienda seguir leyendo para procesar datos finales y mensajes posteriores al handshake. Repetir el apagado sin drenar datos de aplicación pendientes puede fallar.
El estado “sent” sigue el intento de envío; “received” corresponde a la alerta remota. Quiet shutdown marca localmente el cierre sin transmitir alerta y no cumple el protocolo. Desde OpenSSL 3.0, un EOF inesperado conserva una clasificación de error. SSL_OP_IGNORE_UNEXPECTED_EOF solo es justificable si la capa superior detecta truncamiento de forma inequívoca y ejecuta esa verificación.
GnuTLS diferencia GNUTLS_SHUT_WR de GNUTLS_SHUT_RDWR: enviar el cierre no equivale a esperar al par. BoringSSL mantiene por separado el estado de lectura y escritura. Las bibliotecas exponen la evidencia; una métrica plana puede borrarla.
HTTP posee la frontera que TLS no tiene
HTTP/1.1 decide la completitud con su framing. Content-Length exige la cantidad declarada de octetos. El modo chunked exige el fragmento cero terminal. Si falta, el cierre de la conexión no convierte el cuerpo en completo.
Las respuestas delimitadas solo por cierre son más delicadas. Su final depende de que la conexión termine correctamente; por eso una terminación TLS incompleta deja incompleto el mensaje. RFC 9112 recomienda delimitación explícita porque un fallo de red puede parecer un cierre normal.
Si una longitud o chunk terminal ya se verificó, esa evidencia sigue siendo válida aunque después falte la alerta remota. Esa es la prueba necesaria para tolerar ciertos EOF: cada mensaje sobre el que se actuará debe tener un límite independiente, no solo una opción de compatibilidad activada.
El multiplexado necesita un límite de solicitud
HTTP/2 comparte una conexión TLS entre muchos streams. close_notify no revela qué peticiones empezó a procesar el servidor. GOAWAY añade un último identificador que acota qué streams pudieron ser tratados. Los superiores pueden reintentarse; los incluidos en el límite siguen siendo inciertos.
Si la conexión termina sin GOAWAY, un POST no idempotente en vuelo no obtiene una respuesta semántica del cierre TLS. HTTP/3 preserva la misma idea sobre QUIC: su GOAWAY acota peticiones aceptadas. La terminación de una conexión nunca inventa un acuse por solicitud.
Reintentar a ciegas puede duplicar pagos; negarse a reintentar puede perder trabajo. La decisión requiere semántica del método, clave de idempotencia, consulta de resultado o reconciliación.
QUIC no usa este cierre
QUIC emplea TLS para el handshake, pero no protege los datos de aplicación mediante registros TLS. Traduce las alertas TLS a errores de conexión y dispone de CONNECTION_CLOSE, FIN de stream, reset, estados de cierre y drenaje. La semántica de advertencia de close_notify no es portátil a QUIC.
HTTP/3 necesita GOAWAY porque tampoco un cierre QUIC basta para saber qué solicitudes fueron aceptadas. Los paneles deben decir qué observaron: alerta TLS, FIN o RST TCP, FIN de stream QUIC, CONNECTION_CLOSE, timeout inactivo, GOAWAY HTTP o acuse de aplicación.
Evidencia para autorizar un final
Registrar rol y dirección del extremo, versión TLS, correlación y último mensaje o stream completo. Conservar regla de framing, bytes esperados y recibidos, identificador de petición, clave de idempotencia y clase de reintento.
Para TLS, separar alerta local encolada y despachada, alerta remota recibida, secuencia de retornos, estados de lectura y escritura, texto o cifrado pendiente y clasificación exacta del error. Incluir quiet mode, política de EOF, versión de biblioteca, kTLS y proxies.
Para el transporte, distinguir FIN, RST, EOF, timeout y medio cierre. Para el negocio, guardar acuse, identificador de commit, tiempo durable y la autoridad que lo emitió. La secuencia debe permitir explicar el riesgo de repetir una operación.
Las pruebas negativas deben cortar TCP antes de la alerta, omitir el chunk terminal, cerrar tras el mensaje pero antes del commit, observar el retorno 0, dejar datos remotos pendientes, activar quiet shutdown, alternar el tratamiento del EOF y terminar HTTP/2 o HTTP/3 con una petición no idempotente en vuelo.
Fuentes
- https://www.rfc-editor.org/rfc/rfc9846.html
- https://www.rfc-editor.org/rfc/rfc9293.html
- https://www.rfc-editor.org/rfc/rfc9112.html
- https://www.rfc-editor.org/rfc/rfc9113.html
- https://www.rfc-editor.org/rfc/rfc9000.html
- https://www.rfc-editor.org/rfc/rfc9001.html
- https://www.rfc-editor.org/rfc/rfc9114.html
- https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
- https://docs.openssl.org/3.5/man3/SSL_shutdown/
- https://docs.openssl.org/master/man3/SSL_get_error/
- https://docs.openssl.org/master/man3/SSL_CTX_set_options/
- https://www.gnutls.org/manual/html_node/Core-TLS-API.html
- https://boringssl.googlesource.com/boringssl/+/refs/heads/main/ssl/ssl_lib.cc
- https://heng.lu/running-code-primary-the-patch-needed-to-preserve-the-internet-original-design/
Informe para miembros
Contexto ampliado del perfil
Inicia sesión con el nivel de membresía adecuado para desbloquear el informe completo y las notas de las fuentes.
Solo para Strategic Circle
Strategic Circle
Abierto a todos los lectores. Desbloquea informes de perfil después de unirte e iniciar sesión.
Únete a Strategic CircleSolo para Leadership Alliance
Leadership Alliance
Para propietarios y directivos cualificados de activos de propiedad intelectual; inicia sesión para desbloquear los informes de la alianza.
Unirse a Leadership Alliance