Error: no se puede verificar el primer certificado en nodejs

141

Estoy tratando de descargar un archivo del servidor jira usando una url pero recibo un error. Cómo incluir el certificado en el código para verificar el error:

Error: unable to verify the first certificate in nodejs

at Error (native)
    at TLSSocket.<anonymous> (_tls_wrap.js:929:36)

  at TLSSocket.emit (events.js:104:17)

at TLSSocket._finishInit (_tls_wrap.js:460:8)

Mi código Nodejs:

var https = require("https");
var fs = require('fs');
var options = {
    host: 'jira.example.com',
    path: '/secure/attachment/206906/update.xlsx'
};

https.get(options, function (http_res) {

    var data = "";


    http_res.on("data", function (chunk) {

        data += chunk;
    });


    http_res.on("end", function () {

        var file = fs.createWriteStream("file.xlsx");
        data.pipe(file);

    });
});
Labeo
fuente
¿Pudiste resolver esto?
Sharad Jain
1
Utilicé otro procedimiento como deshabilitar la verificación del certificado y terminé
Labeo
¿puedes elaborar un poco más? Esto será realmente útil para mí
sharad jain
vea la respuesta a continuación para la validación del certificado que necesitamos rechazar No autorizado
Labeo

Respuestas:

120

Intente agregar el certificado raíz apropiado

Esta siempre será una opción mucho más segura que simplemente aceptar ciegamente puntos finales no autorizados, que a su vez solo deberían usarse como último recurso.

Esto puede ser tan simple como agregar

require('https').globalAgent.options.ca = require('ssl-root-cas/latest').create();

a su aplicación

El paquete npm de las CA raíz de SSL (como se usa aquí) es un paquete muy útil con respecto a este problema.

Joshua
fuente
9
Esta respuesta debe usarse en la mayoría de los casos, ya que realmente soluciona el problema en lugar de deshabilitar todo el beneficio de SSL.
mikemaccana
12
Como se indica en el módulo README ssl-root-cas, una de las causas más comunes de este problema es que su certificado no incorpora sus certificados de CA intermedios. Intente arreglar su certificado antes de intentar cualquier otra cosa;)
Laurent VB
Es posible que ni siquiera necesite el paquete SSL-root-cas. Simplemente configure globalAgents.option.cert en un certificado de cadena completa. Eso fue lo que resolvió mi problema.
smartexpert
1
mkcert no crea un certificado de "cadena completa". Debe concatenar su certificado con el certificado raíz disponible $(mkcert -CAROOT)/rootCA.pemen un nuevo archivo de certificado y hacer algo como https.globalAgent.options.ca = fs.readFileSync('fullchain.pem')Ver github.com/FiloSottile/mkcert/issues/76
Frosty Z
Para ssl-root-casaquellos con mentalidad de seguridad, el módulo npm tiene una solicitud para gz.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/… . Probablemente sea seguro porque es Mozilla, pero parece un vector de ataque.
Avindra Goolcharan
60

Otro truco sucio, que hará que todas tus solicitudes sean inseguras:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
Satara
fuente
8
Esto no parece diferente de la respuesta de Labeo anterior , tan peligroso.
ocramot
44
Es diferente, no requiere ningún cambio de codificación ya que la variable env se puede establecer fuera del código fuente.
jzacharuk
1
Esta respuesta es peligrosa. Está desactivando cualquier seguridad que brinde TLS.
Flimm
1
Esto funcionó para mí, muy útil. En mi caso, solo estoy hablando con localhost , por lo que la seguridad no es el problema.
Mike S
Bien, de hecho, solo para probar localhost. Solo asegúrate de eliminarlo después de tus pruebas.
Nico
44

para no poder verificar el primer certificado en nodejs se necesita rechazar sin autorización

 request({method: "GET", 
        "rejectUnauthorized": false, 
        "url": url,
        "headers" : {"Content-Type": "application/json",
        function(err,data,body) {
    }).pipe(
       fs.createWriteStream('file.html'));
Labeo
fuente
129129
Esta respuesta es peligrosa. El otro es más seguro.
mikemaccana
3
Bueno, al hacer eso, elimina la seguridad proporcionada por SSL, por lo que debe usarse solo para el desarrollo.
Sylvain
11
No verificar los certificados significa que no puede estar seguro de la identidad de la otra parte y, por lo tanto, puede estar sujeto a un host falsificado. Sin embargo, incluso si no verifica los certificados, recibirá comunicaciones cifradas que no se pueden espiar (fácilmente). Por lo tanto, agregar esta línea no "elimina la seguridad" de SSL ni, como dijo otro comentarista, "deshabilita [] todo el beneficio de SSL".
Bob Pollack el
44
Deshabilitar la verificación SSL NO es una solución a ningún problema. :-)
Siddhu
9
Esto funciona si está utilizando la biblioteca de solicitud de nodo. Que soy yo. Y gracias, resuelve mi necesidad inmediata de desarrollo.
Alan
29

El servidor desde el que intenta descargar puede estar mal configurado. Incluso si funciona en su navegador, es posible que no incluya todos los certificados públicos en la cadena necesarios para que un cliente con caché vacío verifique.

Recomiendo revisar el sitio en la herramienta SSLlabs: https://www.ssllabs.com/ssltest/

Busque este error:

La cadena de certificados de este servidor está incompleta.

Y esto:

Problemas de cadena ......... Incompleto

Flimm
fuente
Recibo este problema (Problemas de cadena ......... Incompleto) para mi certificado que está autorizado por DigiCert Inc., ¿cuál es el procedimiento para solucionarlo?
imarchuang
@imarchuang En resumen, su servidor necesita servir no solo el certificado de su dominio, sino también los certificados intermedios. No puedo incluir más detalles en este comentario, pero espero que sea suficiente información para orientarlo en la dirección correcta.
Flimm
muchas gracias, lo descubrimos combinando también el
certificado
¡Gracias! Descubrí que mi certificado estaba incompleto, aunque funcionaba perfectamente en Chrome y Firefox, pero no funcionaba en la aplicación de electrones, y lo arreglé en nginx al ladocat domainname.crt domainname.ca-bundle > domainname-ssl-bundle.crt
Ivan Borshchov
26

unable to verify the first certificate

La cadena de certificados está incompleta.

Significa que el servidor web al que se está conectando está mal configurado y no incluyó el certificado intermedio en la cadena de certificados que le envió.

Cadena de certificados

Lo más probable es que se vea de la siguiente manera:

  1. Certificado de servidor: almacena un certificado firmado por intermediario.
  2. Certificado intermedio: almacena un certificado firmado por la raíz.
  3. Certificado raíz: almacena un certificado autofirmado.

El certificado intermedio debe instalarse en el servidor, junto con el certificado del servidor.
Los certificados raíz están integrados en las aplicaciones de software, navegadores y sistemas operativos.

La aplicación que sirve el certificado tiene que enviar la cadena completa, esto significa el certificado del servidor en sí y todos los intermedios. Se supone que el cliente debe conocer el certificado raíz.

Recrear el problema

Vaya a https://incomplete-chain.badssl.com usando su navegador.

No muestra ningún error (el candado en la barra de direcciones es verde).
Es porque los navegadores tienden a completar la cadena si no se envía desde el servidor.

Ahora, conéctese a https://incomplete-chain.badssl.com usando Node:

// index.js
const axios = require('axios');

axios.get('https://incomplete-chain.badssl.com')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Registros: " Error: no se puede verificar el primer certificado ".

Solución

Debe completar la cadena de certificados usted mismo.

Para hacer eso:

1: Necesita obtener el certificado intermedio que falta en .pemformato, luego

2a: extienda el almacén de certificados incorporado de Node usando NODE_EXTRA_CA_CERTS,

2b: o pase su propio paquete de certificados (intermedios y raíz) usando la caopción

1. ¿Cómo obtengo el certificado intermedio?

Usando openssl(viene con Git para Windows ).

Guarde los detalles del certificado del servidor remoto:

openssl s_client -connect incomplete-chain.badssl.com:443 -servername incomplete-chain.badssl.com | tee logcertfile

Estamos buscando el emisor (el certificado intermedio es el emisor / firmante del certificado del servidor):

openssl x509 -in logcertfile -noout -text | grep -i "issuer"

Debería proporcionarle el URI del certificado de firma. Descargalo:

curl --output intermediate.crt http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt

Finalmente, conviértalo a .pem:

openssl x509 -inform DER -in intermediate.crt -out intermediate.pem -text

2a. NODE_EXTRA_CERTS

Estoy usando cross-env para establecer variables de entorno en el package.jsonarchivo:

"start": "cross-env NODE_EXTRA_CA_CERTS=\"C:\\Users\\USERNAME\\Desktop\\ssl-connect\\intermediate.pem\" node index.js"

2b. caopción

Esta opción sobrescribirá las CA raíz integradas del nodo.

Es por eso que necesitamos crear nuestra propia CA raíz. Use ssl-root-cas .

Luego, cree un httpsagente personalizado configurado con nuestro paquete de certificados (raíz e intermedio). Pase este agente a axioscuando haga una solicitud.

// index.js
const axios = require('axios');
const path = require('path');
const https = require('https');
const rootCas = require('ssl-root-cas').create();

rootCas.addFile(path.resolve(__dirname, 'intermediate.pem'));
const httpsAgent = new https.Agent({ca: rootCas});

axios.get('https://incomplete-chain.badssl.com', { httpsAgent })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

En lugar de crear un httpsagente personalizado y pasárselo axios, puede colocar los certificados en el httpsagente global:

// Applies to ALL requests (whether using https directly or the request module)
https.globalAgent.options.ca = rootCas;

Recursos:

  1. https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded
  2. https://www.npmjs.com/package/ssl-root-cas
  3. https://github.com/nodejs/node/issues/16336
  4. https://www.namecheap.com/support/knowledgebase/article.aspx/9605/69/how-to-check-ca-chain-installation
  5. /superuser/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file/
  6. Cómo convertir .crt a .pem
sch
fuente
Explicación muy detallada.
Siete
¡Absolutamente increible! No funcionó para mí, ¡pero qué detalle!
Tom Chadaravicius
6

Esto realmente lo resolvió para mí, desde https://www.npmjs.com/package/ssl-root-cas

// INCORRECT (but might still work)
var server = https.createServer({
  key: fs.readFileSync('privkey.pem', 'ascii'),
  cert: fs.readFileSync('cert.pem', 'ascii') // a PEM containing ONLY the SERVER certificate
});

// CORRECT (should always work)
var server = https.createServer({
  key: fs.readFileSync('privkey.pem', 'ascii'),
  cert: fs.readFileSync('fullchain.pem', 'ascii') // a PEM containing the SERVER and ALL INTERMEDIATES
});
koolaang
fuente
1
Esa es la mejor solución en mi humilde opinión, ya que no requiere bibliotecas adicionales y es simple
Martin Schneider
4

Puede hacerlo modificando las opciones de solicitud como se muestra a continuación. Si está utilizando un certificado autofirmado o un intermediario faltante, establecer estrictoSSL en falso no obligará al paquete de solicitud a validar el certificado.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}
Sundar
fuente
Esto resolvió mi problema, estoy usando el módulo 'solicitud' en lugar del 'http'. ¡Gracias!
Bruno Nunes
2

GoDaddy SSL CCertificate

He experimentado esto al intentar conectarme a nuestro servidor API back-end con el certificado GoDaddy y aquí está el código que usé para resolver el problema.

var rootCas = require('ssl-root-cas/latest').create();

rootCas
  .addFile(path.join(__dirname, '../config/ssl/gd_bundle-g2-g1.crt'))
  ;

// will work with all https requests will all libraries (i.e. request.js)
require('https').globalAgent.options.ca = rootCas;

PD:

Use el certificado incluido y no olvide instalar la biblioteca npm install ssl-root-cas

Dean Christian Armada
fuente
1
esto funcionó para mí, excepto que durante la importación tuve que usar "ssl-root-cas" en lugar de "ssl-root-cas / latest".
krishnan
2

Esto funcionó para mí => agregar agente y 'rechazar no autorizado' establecido en falso

const https = require('https'); //Add This
const bindingGridData = async () => {
  const url = `your URL-Here`;
  const request = new Request(url, {
    method: 'GET',
    headers: new Headers({
      Authorization: `Your Token If Any`,
      'Content-Type': 'application/json',
    }),
    //Add The Below
    agent: new https.Agent({
      rejectUnauthorized: false,
    }),
  });
  return await fetch(request)
    .then((response: any) => {
      return response.json();
    })
    .then((response: any) => {
      console.log('response is', response);
      return response;
    })
    .catch((err: any) => {
      console.log('This is Error', err);
      return;
    });
};

Vigneshwaran Ethirajan
fuente
1

Otro enfoque para resolver esto es usar el siguiente módulo.

node_extra_ca_certs_mozilla_bundle

Este módulo puede funcionar sin ninguna modificación de código al generar un archivo PEM que incluye todos los certificados raíz e intermedios confiables por Mozilla. Puede usar la siguiente variable de entorno (Funciona con Nodejs v7.3 +),

NODE_EXTRA_CA_CERTS

Para generar el archivo PEM para usar con la variable de entorno anterior. Puede instalar el módulo usando:

npm install --save node_extra_ca_certs_mozilla_bundle

y luego inicie su script de nodo con una variable de entorno.

NODE_EXTRA_CA_CERTS=node_modules/node_extra_ca_certs_mozilla_bundle/ca_bundle/ca_intermediate_root_bundle.pem node your_script.js

Otras formas de usar el archivo PEM generado están disponibles en:

https://github.com/arvind-agarwal/node_extra_ca_certs_mozilla_bundle

NOTA: Soy el autor del módulo anterior.

arva
fuente
-3

Estaba usando el módulo nodemailer npm. El siguiente código resolvió el problema

     tls: {
     // do not fail on invalid certs
     rejectUnauthorized: false
     }
Chandru
fuente