Błąd: nie można zweryfikować pierwszego certyfikatu w nodejs

150

Próbuję pobrać plik z serwera Jira przy użyciu adresu URL, ale pojawia się błąd. jak dołączyć certyfikat do kodu, aby zweryfikować błąd:

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)

Mój kod 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
źródło
byłeś w stanie to rozwiązać?
sharad jain
2
Użyłem innej procedury, takiej jak wyłączenie weryfikacji certyfikatu i zrobiłem
Labeo
czy możesz rozwinąć trochę więcej? To będzie dla mnie bardzo pomocne
sharad jain
patrz poniżej odpowiedź dotycząca weryfikacji certyfikatu, który musimy odrzucićUnauthorized
Labeo

Odpowiedzi:

130

Spróbuj dodać odpowiedni certyfikat główny

To zawsze będzie znacznie bezpieczniejsza opcja niż ślepe akceptowanie nieautoryzowanych punktów końcowych, które z kolei powinny być używane tylko w ostateczności.

Może to być tak proste, jak dodanie

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

do swojej aplikacji.

Pakiet npm SSL głównej CA (jak tutaj stosowane) jest bardzo przydatny pakiet dotyczące tego problemu.

Joshua
źródło
9
Tej odpowiedzi należy używać w większości przypadków, ponieważ w rzeczywistości rozwiązuje ona problem, a nie wyłącza wszystkie zalety SSL.
mikemaccana
13
Jak stwierdzono w pliku README modułu ssl-root-cas, jedną z najczęstszych przyczyn tego problemu jest to, że certyfikat nie zawiera osadzonych certyfikatów pośredniego urzędu certyfikacji. Spróbuj naprawić swój certyfikat, zanim spróbujesz cokolwiek innego;)
Laurent VB
Możesz nawet nie wymagać pakietu SSL-root-cas. Po prostu ustaw globalAgents.option.cert na certyfikat fullchain. To rozwiązało mój problem.
smartexpert
4
mkcert nie tworzy certyfikatu „fullchain”. Musisz połączyć swój certyfikat z certyfikatem głównym dostępnym pod adresem $(mkcert -CAROOT)/rootCA.pemw nowym pliku certyfikatu i zrobić coś takiego: https.globalAgent.options.ca = fs.readFileSync('fullchain.pem')Zobacz github.com/FiloSottile/mkcert/issues/76
Frosty Z
1
Ze względów bezpieczeństwa, ssl-root-casmoduł npm ma żądanie do mozilla.org zakodowane na sztywno git.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/… . Prawdopodobnie jest to bezpieczne, ponieważ Mozilla, ale wygląda na wektor ataku.
Avindra Goolcharan
73

Kolejny brudny hack, który sprawi, że wszystkie twoje żądania będą niepewne:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
Satara
źródło
8
Wydaje się, że nie różni się to od odpowiedzi Labeo powyżej , jest równie niebezpieczne.
ocramot
4
Jest inny, nie wymaga żadnych zmian w kodowaniu, ponieważ zmienną env można ustawić poza kodem źródłowym.
jzacharuk
1
Ta odpowiedź jest niebezpieczna. Wyłączasz wszelkie zabezpieczenia zapewniane przez TLS.
Flimm
4
To zadziałało dla mnie, super pomocne. W moim przypadku rozmawiam tylko z localhost , więc bezpieczeństwo nie jest problemem.
Mike S
Rzeczywiście dobrze, aby przetestować localhost. Tylko pamiętaj, aby usunąć go po testach.
Nico
46

unable to verify the first certificate

Łańcuch certyfikatów jest niekompletny.

Oznacza to, że serwer sieciowy, z którym się łączysz, jest źle skonfigurowany i nie zawiera certyfikatu pośredniego w łańcuchu certyfikatów, który do Ciebie wysłał.

Łańcuch certyfikatów

Najprawdopodobniej wygląda to następująco:

  1. Certyfikat serwera - przechowuje certyfikat podpisany przez pośrednika.
  2. Certyfikat pośredni - przechowuje certyfikat podpisany przez administratora.
  3. Certyfikat główny - przechowuje certyfikat z podpisem własnym.

Certyfikat pośredni powinien być zainstalowany na serwerze wraz z certyfikatem serwera.
Certyfikaty główne są wbudowane w aplikacje, przeglądarki i systemy operacyjne.

Aplikacja obsługująca certyfikat musi wysłać cały łańcuch, czyli sam certyfikat serwera i wszystkie pośredniki. Certyfikat główny powinien być znany klientowi.

Odtwórz problem

Przejdź do https://incomplete-chain.badssl.com za pomocą przeglądarki.

Nie pokazuje żadnego błędu (kłódka w pasku adresu jest zielona).
Dzieje się tak, ponieważ przeglądarki zwykle uzupełniają łańcuch, jeśli nie jest on wysyłany z serwera.

Teraz połącz się z https://incomplete-chain.badssl.com za pomocą 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);
  });

Dzienniki: „ Błąd: nie można zweryfikować pierwszego certyfikatu ”.

Rozwiązanie

Musisz samodzielnie uzupełnić łańcuch certyfikatów.

Aby to zrobić:

1: W takim razie musisz zdobyć brakujący certyfikat pośredni w .pemformacie

2a: rozszerz wbudowany magazyn certyfikatów Node za pomocą NODE_EXTRA_CA_CERTS,

2b: lub przekaż własny pakiet certyfikatów (pośrednie i root) za pomocą caopcji.

1. Jak uzyskać certyfikat pośredni?

Używanie openssl(dostarczane z Git dla Windows ).

Zapisz szczegóły certyfikatu zdalnego serwera:

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

Poszukujemy wystawcy (certyfikat pośredni to wystawca / podpisujący certyfikat serwera):

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

Powinien dać Ci identyfikator URI certyfikatu podpisującego. Pobierz to:

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

Na koniec przekonwertuj go na .pem:

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

2a. NODE_EXTRA_CERTS

Używam cross-env do ustawiania zmiennych środowiskowych w package.jsonpliku:

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

2b. caopcja

Ta opcja spowoduje nadpisanie wbudowanych głównych urzędów certyfikacji węzła.

Dlatego musimy stworzyć własny główny urząd certyfikacji. Użyj ssl-root-cas .

Następnie utwórz niestandardowego httpsagenta skonfigurowanego za pomocą naszego pakietu certyfikatów (root i pośredni). Przekaż tego agenta axiospodczas składania wniosku.

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

Zamiast tworzyć niestandardowego httpsagenta i przekazywać go axios, możesz umieścić certyfikaty w httpsglobalnym agencie:

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

Zasoby:

  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. Jak przekonwertować .crt na .pem
sch
źródło
Bardzo szczegółowe wyjaśnienie.
Siódmy
3
Absolutnie wspaniałe! U mnie nie działało, ale co za szczegół!
Tom Chadaravicius
Dziękuję Ci. Pomogło mi to rozwiązać problem z dostępem do określonej witryny, a jednocześnie pomogło mi zrozumieć, dlaczego problem występuje!
Bachmann
44

za niemożność zweryfikowania pierwszego certyfikatu w nodejs konieczne jest odrzucenie nieautoryzowanego

 request({method: "GET", 
        "rejectUnauthorized": false, 
        "url": url,
        "headers" : {"Content-Type": "application/json",
        function(err,data,body) {
    }).pipe(
       fs.createWriteStream('file.html'));
Labeo
źródło
131
Ta odpowiedź jest niebezpieczna. Drugi jest bezpieczniejszy.
mikemaccana
3
Robiąc to, usuwasz zabezpieczenia zapewniane przez SSL, więc powinno być używane tylko do programowania.
Sylvain B
11
Brak sprawdzania certyfikatów oznacza, że ​​nie możesz mieć pewności co do tożsamości drugiej strony, więc możesz podlegać fałszywemu hostowi. Jednak nawet jeśli nie sprawdzasz certyfikatów, nadal otrzymujesz zaszyfrowaną komunikację, której nie można (łatwo) szpiegować. Zatem dodanie tej linii nie „usuwa zabezpieczenia” SSL ani, jak powiedział inny komentator, „wyłącza [] wszystkie korzyści SSL”.
Bob Pollack
4
Wyłączenie weryfikacji SSL NIE jest rozwiązaniem żadnego problemu. :-)
Siddhu,
10
Działa to, jeśli używasz biblioteki żądań węzłów. Kim jestem. Dziękuję, rozwiązuje to moją pilną potrzebę rozwoju.
Alan
29

Serwer, z którego próbujesz pobrać, może być źle skonfigurowany. Nawet jeśli działa w Twojej przeglądarce, może nie zawierać wszystkich publicznych certyfikatów w łańcuchu potrzebnych do weryfikacji przez klienta z pustą pamięcią podręczną.

Polecam sprawdzić stronę w narzędziu SSLlabs: https://www.ssllabs.com/ssltest/

Poszukaj tego błędu:

Łańcuch certyfikatów tego serwera jest niekompletny.

I to:

Problemy z łańcuchem ......... Niekompletne

Flimm
źródło
Otrzymuję ten problem (Problemy z łańcuchem ......... Niekompletny) dla mojego certyfikatu autoryzowanego przez DigiCert Inc., jaka jest procedura, aby to naprawić?
imarchuang
@imarchuang Krótko mówiąc, serwer musi obsługiwać nie tylko certyfikat dla Twojej domeny, ale także certyfikaty pośrednie. Nie mogę podać więcej szczegółów w tym komentarzu, ale mam nadzieję, że to wystarczająca ilość informacji, aby wskazać właściwy kierunek.
Flimm,
wielkie dzięki, zorientowaliśmy się, przeczesując również certyfikat główny
imarchuang
Dzięki Ci! Odkryłem, że mój certyfikat był niekompletny, chociaż działał doskonale w Chrome i Firefox, ale nie działał w aplikacji electron, i naprawiłem go na stronie nginxcat domainname.crt domainname.ca-bundle > domainname-ssl-bundle.crt
Ivan Borshchov
7

To faktycznie rozwiązało to dla mnie, od 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
źródło
1
To najlepsze rozwiązanie imho, ponieważ nie wymaga dodatkowych bibliotek i jest proste
Martin Schneider
4

Możesz to zrobić, modyfikując opcje żądania, jak poniżej. Jeśli używasz certyfikatu z podpisem własnym lub brakującego pośrednika, ustawienie parametru strictSSL na false nie wymusi żądania pakietu w celu zweryfikowania certyfikatu.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}
Sundar
źródło
To rozwiązało mój problem, używam modułu „request” zamiast „http”. Dzięki!
Bruno Nunes
3

To zadziałało dla mnie => dodawanie agenta i 'odrzucenieUnauthorized' ustawione na 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
źródło
Ważne jest, aby nie usuwać zabezpieczeń ...
Daniel W.
2

Certyfikat GoDaddy SSL CCertificate

Doświadczyłem tego, próbując połączyć się z naszym serwerem API zaplecza z certyfikatem GoDaddy i oto kod, którego użyłem do rozwiązania problemu.

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:

Skorzystaj z dołączonego certyfikatu i nie zapomnij zainstalować biblioteki npm install ssl-root-cas

Dziekan Christian Armada
źródło
1
to zadziałało dla mnie, z wyjątkiem tego, że podczas importowania musiałem użyć „ssl-root-cas” zamiast „ssl-root-cas / latest”.
krishnan
2

Innym sposobem rozwiązania tego problemu jest użycie następującego modułu.

node_extra_ca_certs_mozilla_bundle

Moduł ten może działać bez modyfikacji kodu, generując plik PEM zawierający wszystkie certyfikaty główne i pośrednie zaufane przez Mozillę. Możesz użyć następującej zmiennej środowiskowej (działa z Nodejs v7.3 +),

NODE_EXTRA_CA_CERTS

Aby wygenerować plik PEM do użycia z powyższą zmienną środowiskową. Moduł możesz zainstalować za pomocą:

npm install --save node_extra_ca_certs_mozilla_bundle

a następnie uruchom skrypt węzła ze zmienną środowiskową.

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

Inne sposoby wykorzystania wygenerowanego pliku PEM są dostępne pod adresem:

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

UWAGA: jestem autorem powyższego modułu.

arva
źródło
-4

Używałem modułu Nodemailer npm. Poniższy kod rozwiązał problem

     tls: {
     // do not fail on invalid certs
     rejectUnauthorized: false
     }
Chandru
źródło