Erro: não foi possível verificar o primeiro certificado no nodejs

141

Estou tentando baixar um arquivo do servidor jira usando um URL, mas estou recebendo um erro. como incluir o certificado no código para verificar o erro:

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)

Meu 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
fonte
você foi capaz de resolver isso?
sharad jain
1
i usado outro procedimento como desabilitar a verificação do certificado e feito
Labeo
você pode elaborar um pouco mais? Isso vai ser realmente útil para mim
sharad jain
veja abaixo resposta para validação de certificado precisamos ter rejectUnauthorized
Labeo

Respostas:

120

Tente adicionar o certificado raiz apropriado

Essa sempre será uma opção muito mais segura do que aceitar cegamente pontos finais não autorizados, os quais, por sua vez, devem ser usados ​​apenas como último recurso.

Isso pode ser tão simples quanto adicionar

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

para o seu aplicativo.

O pacote npm de CAs raiz SSL (conforme usado aqui) é um pacote muito útil sobre esse problema.

Joshua
fonte
9
Essa resposta deve ser usada na maioria dos casos, pois corrige o problema em vez de desabilitar todo o benefício do SSL.
Mikemaccana
12
Conforme declarado no README do módulo ssl-root-cas, uma das causas mais comuns para esse problema é que o seu certificado não incorpora seus certificados CA intermediários. Tente consertar seu certificado antes de tentar qualquer outra coisa;);
Laurent VB
Você pode nem precisar do pacote SSL-root-cas. Basta definir o globalAgents.option.cert como um certificado de cadeia completa. Foi isso que resolveu meu problema.
Smartexpert
1
O mkcert não cria um certificado "fullchain". Você tem que concatenar o seu certificado com a raiz cert disponível em $(mkcert -CAROOT)/rootCA.pemum novo arquivo de certificado e fazer algo como https.globalAgent.options.ca = fs.readFileSync('fullchain.pem')See github.com/FiloSottile/mkcert/issues/76
Frosty Z
Para a segurança, o ssl-root-casmódulo npm possui uma solicitação para o mozilla.org codificado em git.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/… . Provavelmente é seguro porque o Mozilla, mas parece um vetor de ataque.
Avindra Goolcharan 14/03
60

Outro truque sujo, que tornará todos os seus pedidos inseguros:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
Satara
fonte
8
Isso não parece diferente da resposta de Labeo acima , tão perigosa quanto.
Ocramot 13/12/19
4
É diferente, não requer alterações de codificação, pois a variável env pode ser definida fora do código-fonte.
Jzacharuk 9/05/19
1
Essa resposta é perigosa. Você está desativando qualquer segurança fornecida pelo TLS.
Flimm 12/09/19
1
Isso funcionou para mim, super útil. No meu caso, estou apenas conversando com o host local , então a segurança não é o problema.
Mike S
Bem, de fato, apenas para testar o host local. Apenas certifique-se de removê-lo após os testes.
Nico
44

para incapaz de verificar o primeiro certificado em nodejs rejeitar não autorizado é necessário

 request({method: "GET", 
        "rejectUnauthorized": false, 
        "url": url,
        "headers" : {"Content-Type": "application/json",
        function(err,data,body) {
    }).pipe(
       fs.createWriteStream('file.html'));
Labeo
fonte
129
Essa resposta é perigosa. O outro é mais seguro.
Mikemaccana
3
Bem, fazendo isso, você remove a segurança fornecida pelo SSL, portanto ela deve ser usada apenas para desenvolvimento.
22416 Sylvain
11
Não verificar os certificados significa que você não pode ter certeza da identidade da outra parte e, portanto, pode estar sujeito a um host falsificado. Mesmo que você não verifique os certificados, no entanto, ainda recebe comunicação criptografada que não pode ser (facilmente) espionada. Portanto, adicionar essa linha não "remove a segurança" do SSL nem, como outro comentarista disse, "desabilita [] todo o benefício do SSL".
Bob Pollack
4
Desativando a verificação SSL não é uma solução para qualquer problema :-).
siddhu
9
Isso funciona se você estiver usando a biblioteca de solicitações de nó. O que eu sou. E obrigado, resolve a minha necessidade imediata de desenvolvimento.
Alan
29

O servidor do qual você está tentando baixar pode estar mal configurado. Mesmo que funcione no seu navegador, talvez não esteja incluindo todos os certificados públicos na cadeia necessários para a verificação de um cliente vazio em cache.

Eu recomendo verificar o site na ferramenta SSLlabs: https://www.ssllabs.com/ssltest/

Procure este erro:

A cadeia de certificados deste servidor está incompleta.

E isto:

Questões em cadeia ......... Incompleto

Flimm
fonte
Recebo esse problema (problemas de cadeia ......... Incompleto) para meu certificado autorizado pela DigiCert Inc., qual é o procedimento para corrigir isso?
Imarchuang
@imarchuang Em resumo, seu servidor precisa servir não apenas o certificado do seu domínio, mas também os certificados intermediários. Não posso incluir mais detalhes neste comentário, mas espero que seja informação suficiente para apontar na direção certa.
Flimm
muito obrigado, nós entendemos por pentear o certificado raiz também
imarchuang
Te agradece! Eu descobri o meu cert foi incompleta, embora ele funcionou perfeitamente no Chrome e Firefox, mas não funcionou no aplicativo de elétrons, e eu fixa-lo no lado nginx porcat domainname.crt domainname.ca-bundle > domainname-ssl-bundle.crt
Ivan Borshchov
26

unable to verify the first certificate

A cadeia de certificados está incompleta.

Isso significa que o servidor da web ao qual você está se conectando está configurado incorretamente e não incluiu o certificado intermediário na cadeia de certificados que ele enviou a você.

Cadeia de certificados

Provavelmente tem a seguinte aparência:

  1. Certificado do servidor - armazena um certificado assinado pelo intermediário.
  2. Certificado intermediário - armazena um certificado assinado pela raiz.
  3. Certificado raiz - armazena um certificado autoassinado.

O certificado intermediário deve ser instalado no servidor, junto com o certificado do servidor.
Os certificados raiz são incorporados aos aplicativos de software, navegadores e sistemas operacionais.

O aplicativo que atende o certificado deve enviar a cadeia completa, isso significa o próprio certificado do servidor e todos os intermediários. O certificado raiz deve ser conhecido pelo cliente.

Recrie o problema

Acesse https://incomplete-chain.badssl.com usando seu navegador.

Não mostra nenhum erro (o cadeado na barra de endereço é verde).
Isso ocorre porque os navegadores tendem a completar a cadeia se não forem enviados do servidor.

Agora, conecte-se a https://incomplete-chain.badssl.com usando o 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);
  });

Logs: " Erro: não é possível verificar o primeiro certificado ".

Solução

Você precisa completar a cadeia de certificados.

Fazer isso:

1: você precisa obter o certificado intermediário ausente no .pemformato e, em seguida,

2a: estenda o armazenamento de certificados interno do Node usando NODE_EXTRA_CA_CERTS,

2b: ou passe seu próprio pacote de certificados (intermediários e raiz) usandoca opção

1. Como obtenho certificado intermediário?

Usando openssl(vem com Git para Windows ).

Salve os detalhes do certificado do servidor remoto:

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

Estamos procurando o emissor (o certificado intermediário é o emissor / assinante do certificado do servidor):

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

Ele deve fornecer o URI do certificado de assinatura. Baixe:

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

Por fim, converta-o para .pem:

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

2a NODE_EXTRA_CERTS

Estou usando o ambiente cruzado para definir variáveis ​​de ambiente no package.jsonarquivo:

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

2b. caopção

Essa opção substituirá as CAs raiz internas do Nó.

É por isso que precisamos criar nossa própria CA raiz. Use ssl-root-cas .

Em seguida, crie um httpsagente personalizado configurado com nosso pacote de certificados (raiz e intermediário). Passe este agente para axiosao fazer uma solicitação.

// 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);
  });

Em vez de criar um httpsagente personalizado e passá-lo para axios, você pode colocar os certificados no 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. Como converter .crt para .pem
sch
fonte
Explicação muito detalhada.
Sete
Absolutamente surpreendente! Não funcionou para mim, mas que detalhe!
Tom Chadaravicius 26/03
6

Isso realmente resolveu para mim, em 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
fonte
1
Essa é a melhor solução imho, pois não requer bibliotecas adicionais e é simples
Martin Schneider
4

Você pode fazer isso modificando as opções de solicitação conforme abaixo. Se você estiver usando um certificado autoassinado ou um intermediário ausente, configurar strictSSL como false não forçará o pacote de solicitação para validar o certificado.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}
Sundar
fonte
Isso resolveu meu problema, estou usando o módulo 'request' em vez do 'http'. Obrigado!
Bruno Nunes
2

Certificado SSL do GoDaddy

Eu experimentei isso ao tentar conectar ao nosso servidor de API back-end com certificado GoDaddy e aqui está o código que eu usei para resolver o 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;

PS:

Use o certificado incluído e não esqueça de instalar a biblioteca npm install ssl-root-cas

Dean Christian Armada
fonte
1
isso funcionou para mim, exceto que, durante a importação, tive que usar "ssl-root-cas" em vez de "ssl-root-cas / latest".
precisa
2

Isso funcionou para mim => adicionando agente e 'rejectUnauthorized' definido como false

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
fonte
1

Outra abordagem para resolver isso é usar o seguinte módulo.

node_extra_ca_certs_mozilla_bundle

Este módulo pode funcionar sem nenhuma modificação de código, gerando um arquivo PEM que inclui todos os certificados raiz e intermediários confiáveis ​​pelo Mozilla. Você pode usar a seguinte variável de ambiente (Works with Nodejs v7.3 +),

NODE_EXTRA_CA_CERTS

Para gerar o arquivo PEM para usar com a variável de ambiente acima. Você pode instalar o módulo usando:

npm install --save node_extra_ca_certs_mozilla_bundle

e, em seguida, inicie o script do nó com uma variável de ambiente.

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

Outras maneiras de usar o arquivo PEM gerado estão disponíveis em:

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

NOTA: Eu sou o autor do módulo acima.

arva
fonte
-3

Eu estava usando o módulo nodemailer npm. O código abaixo resolveu o problema

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