Cómo implementar el pinning de SSL/TLS en Node.js
Nwani Victory
29 de agosto de 2023
0 minutos de lecturaAnte los ataques de intermediario (MITM) que realizan los actores de amenazas, contar con un certificado SSL/TLS ya no es una razón válida para confiar en una conexión entrante. Por eso, cada vez más desarrolladores adoptan el pinning de SSL/TLS, también conocido como pinning de certificados o de claves públicas, como medida adicional para demostrar la autenticidad y la integridad de una conexión.
En una aplicación Node.js, el pinning de SSL/TLS agrega una capa adicional de seguridad al impedir que los atacantes intercepten y manipulen la comunicación entre el cliente y el servidor.
Este artículo explica el pinning de certificados y destaca sus beneficios y casos de uso en aplicaciones Node.js.
Cuándo y por qué usar el pinning de SSL/TLS
El pinning de SSL/TLS es un mecanismo de seguridad que ayuda a proteger contra ataques MITM y otros ataques relacionados con certificados. Para ello, garantiza que un cliente, como una aplicación Node.js, solo se conecte a un servidor que tenga un certificado digital verificado previamente. Esta técnica almacena y usa certificados específicos o claves públicas en las aplicaciones host para compararlos con la clave pública o el certificado del servidor. Podemos codificar los certificados o las claves públicas directamente en nuestras aplicaciones mediante variables de entorno, o almacenarlos de forma externa en un servicio de bóveda de claves para tener más flexibilidad.
El pinning de certificados reduce el riesgo de ataques MITM al considerar fraudulentas y terminar todas las solicitudes que no tengan un certificado fijado coincidente, incluso si usan HTTPS. También ayuda a prevenir otras vulnerabilidades relacionadas con certificados, como la suplantación o manipulación de certificados y el compromiso de las autoridades de certificación (CA).
Según la encuesta para desarrolladores de Stack Overflow, el 47,12 % de los desarrolladores usa Node.js, lo que lo convierte en una tecnología web estándar para aplicaciones del lado del servidor. Los módulos integrados en Node.js, como HTTPS, agregan una capa adicional de protección a una aplicación al validar los certificados fijados. Una aplicación Node.js que usa pinning de certificados compara el certificado presentado durante el protocolo de enlace TLS con el certificado o las claves públicas predefinidos. Si no coinciden, termina la solicitud en la capa de transporte.
Un caso de uso del pinning de certificados son las aplicaciones financieras en dispositivos móviles. Un ataque MITM contra uno de estos dispositivos puede tener consecuencias catastróficas para su propietario, ya que el dispositivo podría transmitir información personal confidencial, como datos de tarjetas de crédito o de contacto. Aunque las versiones más recientes de los sistemas operativos móviles, como Android Pie, incluyen funciones de seguridad que solo permiten a las aplicaciones establecer conexiones seguras, los actores de amenazas podrían lanzar ataques de phishing y generar certificados autofirmados para acceder al dispositivo. El pinning de certificados garantiza que la aplicación no permita una conexión fraudulenta, incluso si el dispositivo confía en el certificado autofirmado.
Preparativos para implementar el pinning de SSL/TLS
Cuando un cliente envía una solicitud de red a un servidor, la capa de transporte inicia un protocolo de enlace seguro mediante el protocolo SSL/TLS. El servidor presenta su certificado digital, que contiene la clave pública que se usa para el cifrado y la autenticación. Durante el protocolo de enlace, la aplicación obtiene la clave pública del servidor a partir del certificado presentado.
La aplicación compara la clave pública obtenida con una copia preconfigurada o codificada directamente. Si coincide con la copia almacenada localmente, la conexión se considera segura y la comunicación puede continuar. Sin embargo, si las claves no coinciden, la aplicación puede terminar la conexión o tomar las medidas correspondientes según su política de seguridad, como generar una alerta o negarse a enviar datos confidenciales.
Las CA desempeñan un papel fundamental en el proceso de pinning de certificados, ya que proporcionan los certificados SSL/TLS necesarios para establecer una conexión segura. La CA firma el certificado que valida el navegador en aplicaciones web o una cadena de confianza del sistema operativo. El pinning de certificados es un paso adicional para confirmar que el certificado es de confianza.
TLS es un módulo integrado en Node.js que puede extraer de un host un objeto de certificado que contiene un campo de clave pública. El siguiente código muestra cómo usar el módulo TLS para hacer una solicitud a un host y recuperar el certificado en un objeto:
En este código, el método getPeerCertificate se encarga de extraer el certificado. También podemos usar el método getPeerX509Certificate para recuperar el certificado del par en un objeto con formato x509Certificate.
Implementación del pinning de SSL/TLS en Node.js
Para usar el pinning de SSL/TLS en una aplicación Node.js, debemos especificar la clave o el certificado en la configuración de la solicitud. En HTTPS, las propiedades ca, cert y key de las opciones de https.request ayudan a implementar el pinning de SSL/TLS. Al fijar un certificado, la propiedad `ca` debe especificar un arreglo que contenga los certificados codificados en formato PEM de privacidad mejorada (PEM) o los certificados de CA de confianza. La propiedad key debe especificar un arreglo que contenga las claves privadas asociadas a un certificado de servidor de confianza.
Snyk Code es una herramienta de pruebas de seguridad de aplicaciones estáticas (SAST) diseñada para desarrolladores que identifica vulnerabilidades de seguridad en una base de código en tiempo real durante el desarrollo. Está disponible mediante comandos de interfaz de línea de comandos (CLI), kits de desarrollo de software (SDK) para pipelines de integración continua y entrega continua (CI/CD), interfaces web, extensiones para entornos de desarrollo integrados (IDE) y editores de código. En un proyecto Node.js, Snyk Code puede detectar y alertarnos cuando falta compatibilidad con certificados SSL/TLS.
Para identificar la falta de compatibilidad con SSL/TLS, usa el siguiente comando code test para analizar la base de código del proyecto desde la terminal. El comando snyk ignore --file-path=<file_path> permite ignorar archivos o directorios que no queremos incluir en el análisis de Snyk, como el directorio node_modules que contiene nuestros paquetes de Node.js:
Aunque el pinning de SSL/TLS ofrece beneficios de seguridad, una implementación deficiente y sin manejo de errores también puede tener consecuencias desastrosas. Nuestras implementaciones de pinning deben contar con un plan a prueba de fallos para manejar situaciones excepcionales, como la revocación o el vencimiento de un certificado fijado. El manejo de errores evita que toda la aplicación se bloquee cuando no puede confirmar un certificado o una clave pública. Las auditorías pueden ayudarnos a registrar estos casos.
Para comenzar, crea un directorio llamado snyk-tls y usa:
El siguiente código muestra una huella digital de clave pública fijada en una aplicación Node.js que coincide con la huella digital de la clave pública del certificado para una solicitud GET creada con el módulo nativo https de Node.js. Si las huellas digitales de las claves públicas no coinciden, el código cancela la solicitud. Crea un archivo index.js y agrega el siguiente código para probarlo:
En el código anterior, se comprueba el arreglo PINNED_CERT_FINGERPRINTS para determinar si la huella digital del certificado público está entre las huellas permitidas. Si no está incluida, el método destroy termina la solicitud.
En producción, debemos almacenar la clave pública o el certificado mediante variables de entorno o en un servicio de bóveda de claves independiente para evitar que la clave pública quede expuesta al enviar el código a un repositorio.
Pruebas y mantenimiento del pinning de SSL/TLS
Una excelente manera de probar la eficacia de una implementación de pinning es simular un ataque MITM. Herramientas como Mitmproxy o Wireshack permiten crear un entorno de prueba para monitorear, interceptar y usar como proxy las solicitudes de red dirigidas a un host de prueba.
Para probar la eficacia de nuestro pinning de SSL/TLS, debemos desactivar los certificados fijados y lanzar un ataque MITM a través de un proxy para registrar el comportamiento de referencia de la aplicación sin pinning. Luego repetimos la prueba con el pinning activado para comprobar si la aplicación puede detectar el proxy y terminar la conexión.
Comparar el hash de la clave del certificado es otra forma de probar el pinning de SSL/TLS. El hash de un certificado es único, ya que una función hash criptográfica transforma datos de tamaño arbitrario en valores de tamaño fijo que representan el certificado. Esto lo convierte en una opción adecuada para validar un certificado. Con el módulo crypto de Node.js, podemos generar un hash a partir del certificado del host y compararlo con el hash esperado de la clave pública.
Una tercera forma de probar nuestro pinning de SSL/TLS es usar herramientas de análisis de red, como Nmap, para realizar un escaneo de puertos SSL. Nmap ofrece el comando ssl-enum-chipers para analizar un puerto SSL en un destino específico.
Mantener y actualizar los certificados o las claves públicas fijados es esencial para preservar la confianza, la seguridad y la integridad de los canales de comunicación. Al mantenerlos actualizados, podemos mitigar los riesgos asociados con el compromiso, el vencimiento y la revocación de certificados, y garantizar conexiones seguras y autenticadas.
Abre la carpeta snyk-tls en la terminal y ejecuta el siguiente comando.
Este comando genera un archivo cert.pem que contiene el certificado público de google.com.
A continuación, crea un archivo main.js en la carpeta del proyecto. Pega el siguiente código.
El código anterior importa los módulos necesarios y define dos variables: certPath y command. certPath define la ruta al archivo del certificado. command contiene el valor del comando de shell para generar la huella digital SHA-256 del certificado.
La función generate_SHA256_fingerprint() usa la función exec para ejecutar el comando openssl que genera la huella digital SHA-256 del certificado. Esta función devuelve una promesa que se resuelve con la huella digital.
La función verifyCertificate() define un objeto de opciones para configurar la solicitud HTTPS. Este objeto especifica hostname, port, path y method. También define la función checkServerIdentity() para verificar el certificado del servidor. Esta función garantiza que el certificado pertenezca al host al que nos conectamos y comprueba si la huella digital coincide con la huella fijada.
Para probar este script, ejecuta el comando node main.js en la terminal. Deberías recibir el siguiente resultado.
Para actualizar un certificado o una clave fijados, abrimos el archivo API y ejecutamos el comando openssl que genera el archivo cert.pem. Si usamos un servicio de bóveda externo para almacenar los certificados o las claves públicas fijados, podemos editar los valores en ese servicio sin modificar el código de la aplicación. Estos son algunos casos en los que es necesario actualizar los certificados fijados:
Vencimiento de certificados o claves públicas
Fugas de datos
Revocación por parte del emisor
Políticas de cumplimiento que exigen a los usuarios rotar el certificado después de un plazo determinado
Conclusión
En este artículo aprendimos a implementar el pinning de SSL/TLS en aplicaciones Node.js. El pinning de SSL/TLS agrega una capa adicional de seguridad al verificar la clave pública o el certificado del servidor con una copia almacenada localmente. Ayuda a proteger nuestras aplicaciones contra ataques MITM y otras vulnerabilidades relacionadas con certificados.
Node.js incluye módulos como HTTPS que admiten el pinning de certificados. Podemos aplicar el pinning de SSL/TLS comparando la clave pública durante el protocolo de enlace TLS y terminando la conexión si no coincide. También es esencial manejar los errores correctamente para evitar que se interrumpa el acceso en situaciones excepcionales, como el vencimiento de un certificado o una clave pública, o su revocación por parte del emisor. Probar el pinning de SSL/TLS en nuestras aplicaciones mediante la simulación de ataques, la comparación de hashes de claves y la actualización de certificados o claves es una práctica recomendada para garantizar su eficacia. Otra práctica recomendada es almacenar las claves públicas de forma segura.
Al aprovechar el pinning de SSL/TLS, podemos reforzar la seguridad general de nuestras aplicaciones Node.js y garantizar una experiencia confiable y segura para los usuarios.
Comienza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.



