Estoy tratando de crear un servidor TLS /cliente de la instalación mediante Node.js 0.8.8 con un certificado auto-firmado.

Esencial de código de servidor parece

var tlsServer = tls.createServer({
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem')
}, function (connection) {
  //[...]
});
tlsServer.listen(3000);

Ahora cuando intento conectar a este servidor, yo uso el siguiente código:

var connection = tls.connect({
  host: '192.168.178.31',
  port: 3000,

  rejectUnauthorized: true,
  ca: [ fs.readFileSync('server-cert.pem') ]
}, function () {
  console.log(connection.authorized);
  console.log(connection.authorizationError);
  console.log(connection.getPeerCertificate());
});

Si puedo quitar la línea de

ca: [ fs.readFileSync('server-cert.pem') ]

desde el código del lado del cliente, Node.js lanza un error diciendo que me DEPTH_ZERO_SELF_SIGNED_CERT. Como tengo entendido que esto es debido al hecho de que es un certificado autofirmado y no hay otra parte que confía en el certificado.

Si puedo quitar

rejectUnauthorized: true,

así, el error se ha ido – pero connection.authorized es igual a false que significa que mi conexión no está cifrada. De todos modos, el uso de getPeerCertificate() puedo acceder al certificado enviado por el servidor. Como quiero hacer cumplir una conexión cifrada, entiendo que no puedo quitar esta línea.

Ahora he leído que puedo usar el ca propiedad para especificar cualquier CA que quiero Node.js para de confianza. El la documentación de la TLS módulo implica que es suficiente para agregar el certificado de servidor para la ca matriz, y entonces todo debería estar bien.

Si hago eso, este error se ha ido, pero tengo una nueva:

Hostname/IP doesn't match certificate's altnames

Para mí, esto significa que el CA es ahora, básicamente, de confianza, de ahí que bien ahora, pero el certificado fue hecho para otro host que el que yo uso.

He creado el certificado de uso de

$ openssl genrsa -out server-key.pem 2048
$ openssl req -new -key server-key.pem -out server-csr.pem
$ openssl x509 -req -in server-csr.pem -signkey server-key.pem -out server-cert.pem

la documentación que implica. A la hora de crear el CSR me preguntan las preguntas habituales, tales como país, estado, … y el nombre común (CN). Como se les dice «en la web» por un certificado SSL que hacer no proporcionar su nombre como CN, pero el nombre de host que desea utilizar.

Y esto probablemente es donde me falla.

Traté de

  • localhost
  • 192.168.178.31
  • eisbaer
  • eisbaer.fritz.box

donde los dos últimos son el nombre local y el local completa el nombre de mi máquina.

Alguna idea de lo que estoy haciendo mal aquí?

  • Nota importante: Este es Node v0.8.8. rejectUnauthorized defecto true.
  • Su conexión es todavía cifrados por defecto, si utiliza rejectUnauthorized: false. Simplemente no verificar la identidad del host al que se está conectando, que podría dejar vulnerable a ataques MITM en SSL.
InformationsquelleAutor Golo Roden | 2012-12-30

5 Comentarios

  1. 16

    Recientemente hubo un además de la node.js que permite reemplazar nombre de host de verificación con una función personalizada. Fue agregado a v0.11.14 y estará disponible en la próxima versión estable (0.12). Ahora puedes hacer algo como:

    var options = {
      host: '192.168.178.31',
      port: 3000,
      ca: [ fs.readFileSync('server-cert.pem') ],
      checkServerIdentity: function (host, cert) {
        return undefined;
      }
    };
    options.agent = new https.Agent(options);
    var req = https.request(options, function (res) {
      //...
    });

    Esta ahora aceptará cualquier servidor de identidad, pero todavía cifrar la conexión y comprobar las claves.

    Nota que en versiones anteriores (por ejemplo,v0.11.14), el checkServerIdentity era devolver un boolean indicando la validez del servidor. Lo que ha cambiado (antes de v4.3.1) a la función returning (no throwing) un error si hay un problema y undefined si no es válido.

    • Estar atrapado en v0.10, tengo mono-patched tls módulo para saltar esta comprobación. Es un muy feo revisión como es mundial, pero es mejor que nada.
    • conjunto de NODE_TLS_REJECT_UNAUTHORIZED=0 var env
    • Sólo para advertir a la gente: NODE_TLS_REJECT_UNAUTHORIZED le permite conectarse a cualquier persona, ya sea tu intención de servidor o no. La conexión está cifrada, pero no es autorizado que a menudo es una cosa muy importante. Y, estableciendo como una variable Global es una muy fuertes manos del método. Acaba de establecer rejectUnauthorized a false en el tls opciones de conexión.
    • Para la posteridad, (v4.3.1 docs)[nodejs.org/dist/latest-v4.x/docs/api/… indican que debe return un Error si hay un problema con la identidad del servidor, y undefined si el servidor pasa el control de identidad. Seguro de cuándo se produjo este cambio.
  2. 9

    En tls.js líneas 112-141, se puede ver que si el nombre de host se utiliza cuando se llama a connect es una dirección IP, el CN del certificado es ignorado y sólo el SANs están siendo utilizados.

    Como mi certificado de no utilizar SANs, verificación falla.

    • Estoy teniendo el mismo problema, pero tengo SAN localhost y 127.0.0.1. No importa si me conecto en contra de nombre de host (‘localhost’) o la dirección IP (‘127.0.0.1’). Para mí, openssl s_client conecta, teniendo en cuenta todos los mismos parámetros, mientras tls.connect() falla.
  3. 7

    Si usted está utilizando un nombre de host para conectarse, el nombre de host se verifican los Nombres Alternativos del Sujeto de tipo DNS, si los hubiere, y caer de nuevo en el CN en el Asunto Nombre completo de otra manera.

    Si usted está utilizando una dirección IP para conectarse, la dirección IP será contrastarse con la SANs de dirección IP tipo, sin caer en la CN.

    Esto es al menos lo implementaciones compatibles con la HTTP sobre TLS especificación (es decir, HTTPS) hacer. Algunos navegador son un poco más tolerantes.

    Este es exactamente el mismo problema que en esta respuesta en Java, que también proporciona un método para poner personalizado SANs través de OpenSSL (ver este documento también).

    Generalmente hablando, a menos que sea para una prueba de CA, es muy difícil de gestionar los certificados basados en direcciones IP. Conexión con un nombre de host es mejor.

  4. 4

    Mitar había una suposición equivocada de que checkServerIdentity debe devolver ‘true’ en el éxito, pero en realidad se debe devolver ‘indefinido’ en el éxito. Los otros valores son tratados como descripciones de error.

    De modo que tal código es correcto:

    var options = {
      host: '192.168.178.31',
      port: 3000,
      ca: [ fs.readFileSync('server-cert.pem') ],
      checkServerIdentity: function (host, cert) {
        //It can be useful to resolve both parts to IP or to Hostname (with some synchronous resolver (I wander why they did not add done() callback as the third parameter)).
        //Be carefull with SNI (when many names are bound to the same IP).
        if (host != cert.subject.CN)
          return 'Incorrect server identity';//Return error in case of failed checking.
          //Return undefined value in case of successful checking.
          //I.e. you could use empty function body to accept all CN's.
      }
    };
    options.agent = new https.Agent(options);
    var req = https.request(options, function (res) {
      //...
    });

    He intentado sólo para hacer una edición en la Mitar de la respuesta, pero la edición fue rechazada, así que he creado un separado respuesta.

  5. -1

    Lo que estás haciendo mal es el uso de una dirección IP en lugar de un nombre de dominio. Crear un nombre de dominio y pegarlo en un servidor DNS (o simplemente en un archivo hosts), crear un certificado auto-firmado con el nombre de dominio, el Nombre Común, y conectar el nombre de dominio en lugar de la dirección IP.

    • Pero debe (debe?) trabajar con direcciones IP así, ¿no? No quiero configurar nombres de dominio, por varias razones.

Dejar respuesta

Please enter your comment!
Please enter your name here