[node.js] 오류 : nodejs에서 첫 번째 인증서를 확인할 수 없습니다

URL을 사용하여 jira 서버에서 파일을 다운로드하려고하는데 오류가 발생합니다. 오류 를 확인하기 위해 코드에 인증서를 포함시키는 방법
:

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)

내 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);

    });
});



답변

적절한 루트 인증서를 추가하십시오

이것은 무단 엔드 포인트를 맹목적으로 받아들이는 것보다 항상 훨씬 안전한 옵션이 될 것이며, 이는 마지막 수단으로 만 사용해야합니다.

이것은 추가하는 것만 큼 간단 할 수 있습니다

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

응용 프로그램에.

SSL 루트 CA는 NPM 패키지 (로 여기에 사용)이 문제에 대한 매우 유용한 패키지입니다.


답변

또 다른 더러운 해킹으로 모든 요청이 안전하지 않게됩니다.

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0


답변

nodejs의 첫 번째 인증서를 확인할 수 없으므로 무단 거부가 필요합니다.

 request({method: "GET",
        "rejectUnauthorized": false,
        "url": url,
        "headers" : {"Content-Type": "application/json",
        function(err,data,body) {
    }).pipe(
       fs.createWriteStream('file.html'));


답변

다운로드하려는 서버가 잘못 구성되었을 수 있습니다. 브라우저에서 작동하더라도 캐시가 비어있는 클라이언트를 확인하는 데 필요한 모든 공용 인증서를 체인에 포함하지 않을 수 있습니다.

SSLlabs 도구에서 사이트를 확인하는 것이 좋습니다. https://www.ssllabs.com/ssltest/

이 오류를 찾으십시오.

이 서버의 인증서 체인이 불완전합니다.

이:

체인 문제 ………… 불완전


답변

unable to verify the first certificate

인증서 체인이 불완전합니다.

연결하려는 웹 서버가 잘못 구성되어 전송 한 인증서 체인에 중간 인증서가 포함되지 않았 음을 의미합니다.

인증서 체인

다음과 같이 보입니다.

  1. 서버 인증서-중간에서 서명 한 인증서를 저장합니다.
  2. 중간 인증서-루트가 서명 한 인증서를 저장합니다.
  3. 루트 인증서-자체 서명 된 인증서를 저장합니다.

중간 인증서는 서버 인증서와 함께 서버에 설치해야합니다.
루트 인증서는 소프트웨어 응용 프로그램, 브라우저 및 운영 체제에 포함되어 있습니다.

인증서를 제공하는 응용 프로그램은 완전한 체인을 보내야합니다. 이는 서버 인증서 자체와 모든 중간체를 의미합니다. 루트 인증서는 클라이언트가 알고 있어야합니다.

문제를 재현

https://incomplete-chain.badssl.com으로 이동브라우저를 사용하여 하십시오.

오류가 표시되지 않습니다 (주소 표시 줄의 자물쇠가 녹색 임). 브라우저가 체인을 완성하는 경향이
있기 때문입니다 가 서버에서 전송되지 않으면 입니다.

이제 Node를 사용하여 https://incomplete-chain.badssl.com에 연결 하십시오 .

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

로그 : ” 오류 : 첫 번째 인증서를 확인할 수 없습니다 “.

해결책

인증서 체인을 직접 작성해야합니다.

하기 위해서:

1 : 누락 된 중간 인증서를 .pem형식으로 가져와야합니다.

A : 확장 노드에 내장 된 인증서 저장소를 사용 NODE_EXTRA_CA_CERTS,

2b : 또는 ca옵션을 사용하여 고유 한 인증서 번들 (중간체 및 루트)을 전달하십시오 .

1. 중간 증명서는 어떻게 받습니까?

사용 openssl( Git for Windows 와 함께 제공 )

원격 서버의 인증서 세부 사항을 저장하십시오.

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

우리는 발급자를 찾고 있습니다 (중간 인증서는 서버 인증서의 발급자 / 서명자입니다).

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

서명 인증서의 URI를 제공해야합니다. 다운로드 해:

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

마지막으로 다음과 같이 변환하십시오 .pem.

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

2a. NODE_EXTRA_CERTS

크로스 환경 을 사용하여 package.json파일 에서 환경 변수를 설정하고 있습니다.

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

2b. ca선택권

이 옵션은 노드의 내장 루트 CA를 덮어 씁니다.

그렇기 때문에 자체 루트 CA를 만들어야합니다. ssl-root-cas를 사용하십시오 .

그런 다음 https인증서 번들 (루트 및 중간)로 구성된 사용자 정의 에이전트를 작성하십시오 . axios요청 시이 에이전트를 전달하십시오 .

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

사용자 정의 https에이전트 를 작성하여로 전달하는 axios대신 https글로벌 에이전트 에 인증서를 배치 할 수 있습니다 .

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

자원:

  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. .crt를 .pem로 변환하는 방법

답변

이것은 실제로 나를 위해 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
});


답변

아래와 같이 요청 옵션을 수정하여이 작업을 수행 할 수 있습니다. 자체 서명 된 인증서 또는 누락 된 중개자를 사용하는 경우 strictSSL을 false로 설정하면 요청 패키지가 인증서의 유효성을 검증하지 않습니다.

var options = {
   host: 'jira.example.com',
   path: '/secure/attachment/206906/update.xlsx',
   strictSSL: false
}