ข้อผิดพลาด: ไม่สามารถตรวจสอบใบรับรองแรกใน nodejs


142

ฉันพยายามดาวน์โหลดไฟล์จากเซิร์ฟเวอร์ jira โดยใช้ url แต่ได้รับข้อผิดพลาด วิธีรวมใบรับรองในรหัสเพื่อตรวจสอบ ข้อผิดพลาด:

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

    });
});

คุณสามารถแก้ปัญหานี้ได้หรือไม่
sharad jain

1
ฉันใช้ขั้นตอนอื่นเช่นปิดใช้งานการตรวจสอบใบรับรองและทำเสร็จแล้ว
Labeo

คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหม สิ่งนี้จะเป็นประโยชน์สำหรับฉันจริงๆ
sharad jain

ดูคำตอบด้านล่างสำหรับการตรวจสอบใบรับรองที่เราจำเป็นต้องปฏิเสธไม่ได้รับอนุญาต
Labeo

คำตอบ:


120

ลองเพิ่มใบรับรองหลักที่เหมาะสม

สิ่งนี้จะเป็นตัวเลือกที่ปลอดภัยกว่าการยอมรับจุดสิ้นสุดที่ไม่ได้รับอนุญาตแบบสุ่มสี่สุ่มห้าซึ่งควรใช้เป็นทางเลือกสุดท้ายเท่านั้น

ซึ่งสามารถทำได้ง่ายๆเพียงแค่เพิ่ม

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

ไปยังแอปพลิเคชันของคุณ

แพคเกจ NPM SSL ราก CAs (ที่ใช้ที่นี่) เป็นแพคเกจที่มีประโยชน์มากเกี่ยวกับปัญหานี้


9
คำตอบนี้ควรใช้ในกรณีส่วนใหญ่เนื่องจากช่วยแก้ไขปัญหาได้จริงแทนที่จะปิดใช้ประโยชน์ทั้งหมดของ SSL
mikemaccana

12
ตามที่ระบุไว้ในโมดูล ssl-root-cas README หนึ่งในสาเหตุที่พบบ่อยที่สุดสำหรับปัญหานี้คือใบรับรองของคุณไม่ได้ฝังใบรับรอง CA ระดับกลาง ลองแก้ไขใบรับรองของคุณก่อนลองทำอย่างอื่น;)
Laurent VB

คุณอาจไม่จำเป็นต้องใช้แพ็คเกจ SSL-root-cas ด้วยซ้ำ เพียงตั้งค่า globalAgents.option.cert เป็นใบรับรอง fullchain นั่นคือสิ่งที่แก้ปัญหาของฉัน
smartexpert

1
mkcertไม่สร้างใบรับรอง "fullchain" คุณต้องเชื่อมต่อใบรับรองของคุณกับใบรับรองหลักที่มีอยู่$(mkcert -CAROOT)/rootCA.pemในไฟล์ใบรับรองใหม่และทำสิ่งต่างๆเช่นhttps.globalAgent.options.ca = fs.readFileSync('fullchain.pem')ดูgithub.com/FiloSottile/mkcert/issues/76
Frosty Z

สำหรับการรักษาความปลอดภัยใจ, ssl-root-casโมดูล NPM มีการร้องขอไปยัง mozilla.org hardcoded git.coolaj86.com/coolaj86/ssl-root-cas.js/src/branch/master/... อาจปลอดภัยเพราะ Mozilla แต่ดูเหมือนว่าเป็นเวกเตอร์การโจมตี
Avindra Goolcharan

61

แฮ็คสกปรกอีกอย่างซึ่งจะทำให้คำขอทั้งหมดของคุณไม่ปลอดภัย:

process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0

8
สิ่งนี้ดูเหมือนจะไม่แตกต่างจากคำตอบของ Labeo ข้างต้นเหมือนอันตราย
ocramot

4
มันแตกต่างกันไม่ต้องการการเปลี่ยนแปลงการเข้ารหัสใด ๆ เนื่องจากสามารถตั้งค่าตัวแปร env นอกซอร์สโค้ดได้
jzacharuk

1
คำตอบนี้อันตราย คุณกำลังปิดใช้งานการรักษาความปลอดภัยที่ TLS มีให้
Flimm

2
สิ่งนี้ได้ผลสำหรับฉันเป็นประโยชน์มาก ในกรณีของฉันฉันแค่คุยกับlocalhostดังนั้นความปลอดภัยจึงไม่ใช่ปัญหา
Mike S

ดีจริง ๆ เพื่อทดสอบ localhost ตรวจสอบให้แน่ใจว่าคุณได้ลบออกหลังการทดสอบ
Nico

44

เนื่องจากไม่สามารถตรวจสอบใบรับรองแรกใน nodejs ปฏิเสธโดยไม่ได้รับอนุญาตเป็นสิ่งจำเป็น

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

130
คำตอบนี้อันตราย อีกอันจะปลอดภัยกว่า
mikemaccana

3
เมื่อทำเช่นนั้นคุณจะลบการรักษาความปลอดภัยที่มีให้โดย SSL ดังนั้นจึงควรใช้เพื่อการพัฒนาเท่านั้น
ซิลเวน

11
การไม่ตรวจสอบใบรับรองหมายความว่าคุณไม่สามารถยืนยันตัวตนของอีกฝ่ายได้ดังนั้นจึงอาจถูกโฮสต์หลอก แม้ว่าคุณจะไม่ได้ตรวจสอบใบรับรองอย่างไรก็ตามคุณยังคงได้รับการสื่อสารที่เข้ารหัสซึ่งไม่สามารถสอดแนมได้ (ง่ายๆ) ดังนั้นการเพิ่มบรรทัดนี้จึงไม่ได้เป็นการ "ลบความปลอดภัย" ของ SSL หรือในขณะที่ผู้แสดงความคิดเห็นรายอื่นกล่าวว่า "ปิดใช้งาน [] ประโยชน์ทั้งหมดของ SSL"
Bob Pollack

4
การปิดใช้งานการตรวจสอบ SSL ไม่ใช่วิธีแก้ปัญหาใด ๆ :-)
Siddhu

9
สิ่งนี้ใช้ได้ถ้าคุณกำลังใช้ไลบรารีคำขอโหนด ซึ่งผม. และขอบคุณมันช่วยแก้ปัญหาความต้องการในการพัฒนาของฉัน
Alan

29

เซิร์ฟเวอร์ที่คุณพยายามดาวน์โหลดอาจได้รับการกำหนดค่าไม่ดี แม้ว่าจะใช้งานได้ในเบราว์เซอร์ของคุณ แต่อาจไม่รวมใบรับรองสาธารณะทั้งหมดในเครือข่ายที่จำเป็นสำหรับไคลเอ็นต์ที่ไม่มีแคชในการตรวจสอบ

ฉันแนะนำให้ตรวจสอบไซต์ในเครื่องมือ SSLlabs: https://www.ssllabs.com/ssltest/

มองหาข้อผิดพลาดนี้:

ห่วงโซ่ใบรับรองของเซิร์ฟเวอร์นี้ไม่สมบูรณ์

และนี่:

ปัญหาเรื่องลูกโซ่ ......... ไม่สมบูรณ์


ฉันได้รับปัญหานี้ (ปัญหาในเครือ ......... ไม่สมบูรณ์) สำหรับใบรับรองของฉันซึ่งได้รับอนุญาตจาก DigiCert Inc. ขั้นตอนในการแก้ไขคืออะไร?
imarchuang

@imarchuang กล่าวโดยย่อคือเซิร์ฟเวอร์ของคุณไม่จำเป็นต้องให้บริการเพียงแค่ใบรับรองสำหรับโดเมนของคุณเท่านั้น แต่ยังรวมถึงใบรับรองระดับกลางด้วย ฉันไม่สามารถใส่รายละเอียดเพิ่มเติมในความคิดเห็นนี้ได้ แต่หวังว่าจะมีข้อมูลเพียงพอที่จะชี้ให้คุณไปในทิศทางที่ถูกต้อง
Flimm

ขอบคุณมากเราคิดออกโดยการหวีใบรับรองรูทด้วย
imarchuang

ขอบคุณ! ฉันพบว่าใบรับรองของฉันไม่สมบูรณ์แม้ว่ามันจะทำงานได้อย่างสมบูรณ์ใน chrome และ firefox แต่ใช้งานไม่ได้ในแอปอิเล็กตรอนและฉันได้แก้ไขใน nginx เคียงข้างcat domainname.crt domainname.ca-bundle > domainname-ssl-bundle.crt
Ivan Borshchov

26

unable to verify the first certificate

ห่วงโซ่ใบรับรองไม่สมบูรณ์

หมายความว่าเว็บเซิร์ฟเวอร์ที่คุณเชื่อมต่อมีการกำหนดค่าไม่ถูกต้องและไม่ได้รวมใบรับรองระดับกลางในสายการรับรองที่ส่งถึงคุณ

ห่วงโซ่ใบรับรอง

มีแนวโน้มมากที่สุดดังนี้:

  1. ใบรับรองเซิร์ฟเวอร์ - จัดเก็บใบรับรองที่ลงนามโดยตัวกลาง
  2. ใบรับรองระดับกลาง - จัดเก็บใบรับรองที่ลงนามโดยรูท
  3. ใบรับรองหลัก - จัดเก็บใบรับรองที่ลงนามด้วยตนเอง

ควรติดตั้งใบรับรองระดับกลางบนเซิร์ฟเวอร์พร้อมกับใบรับรองเซิร์ฟเวอร์
ใบรับรองหลักถูกฝังลงในแอปพลิเคชันซอฟต์แวร์เบราว์เซอร์และระบบปฏิบัติการ

แอปพลิเคชันที่ให้บริการใบรับรองจะต้องส่งห่วงโซ่ที่สมบูรณ์ซึ่งหมายถึงใบรับรองเซิร์ฟเวอร์เองและตัวกลางทั้งหมด ใบรับรองหลักควรเป็นที่รู้จักของลูกค้า

สร้างปัญหาใหม่

ไปที่https://incomplete-chain.badssl.comโดยใช้เบราว์เซอร์ของคุณ

ไม่แสดงข้อผิดพลาดใด ๆ (แม่กุญแจในแถบที่อยู่เป็นสีเขียว)
เป็นเพราะเบราว์เซอร์มีแนวโน้มที่จะดำเนินการให้เสร็จสมบูรณ์หากไม่ได้ส่งจากเซิร์ฟเวอร์

ตอนนี้เชื่อมต่อกับhttps://incomplete-chain.badssl.comโดยใช้ 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);
  });

บันทึก: " ข้อผิดพลาด: ไม่สามารถตรวจสอบใบรับรองแรกได้ "

สารละลาย

คุณต้องกรอกห่วงโซ่ใบรับรองด้วยตัวเอง

ในการทำเช่นนั้น:

1:คุณต้องได้รับใบรับรองกลางที่ขาดหายไปใน.pemรูปแบบจากนั้น

2a:ขยายโหนดของในตัวเก็บใบรับรองใช้NODE_EXTRA_CA_CERTS,

2b:หรือส่งชุดใบรับรองของคุณเอง (ตัวกลางและรูท) โดยใช้caตัวเลือก

1. ฉันจะได้รับใบรับรองระดับกลางได้อย่างไร?

ใช้openssl(มาพร้อมกับGit สำหรับ 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

ฉันใช้cross-envเพื่อตั้งค่าตัวแปรสภาพแวดล้อมในpackage.jsonไฟล์:

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

2b caตัวเลือก

ตัวเลือกนี้จะเขียนทับ CA รากในตัวของ Node

นั่นเป็นเหตุผลที่เราต้องสร้าง root CA ของเราเอง ใช้SSL ราก 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

คำอธิบายละเอียดมาก
7

1
สุดยอดมาก! ไม่ได้ผลสำหรับฉัน แต่เป็นรายละเอียด!
Tom Chadaravicius

6

สิ่งนี้แก้ไขได้จริงสำหรับฉันจาก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
});

1
นั่นเป็นทางออกที่ดีที่สุดเพราะไม่ต้องใช้ไลบรารีเพิ่มเติมและทำได้ง่าย
Martin Schneider

4

คุณสามารถทำได้โดยแก้ไขตัวเลือกคำขอดังต่อไปนี้ หากคุณใช้ใบรับรองที่ลงนามด้วยตนเองหรือตัวกลางที่ขาดหายไปการตั้งค่า secureSSL เป็นเท็จจะไม่บังคับให้แพ็กเกจคำขอตรวจสอบใบรับรอง

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

วิธีนี้ช่วยแก้ปัญหาของฉันได้ฉันใช้โมดูล 'คำขอ' แทน 'http' ขอบคุณ!
Bruno Nunes

2

ใบรับรอง SSL ของ GoDaddy

ฉันเคยประสบกับปัญหานี้ขณะพยายามเชื่อมต่อกับเซิร์ฟเวอร์ API แบ็กเอนด์ด้วยใบรับรอง GoDaddy และนี่คือรหัสที่ฉันใช้ในการแก้ปัญหา

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:

ใช้ใบรับรองที่แถมมาและอย่าลืมติดตั้งไลบรารี npm install ssl-root-cas


1
สิ่งนี้ใช้ได้ผลสำหรับฉันยกเว้นว่าในขณะที่นำเข้าฉันต้องใช้ "ssl-root-cas" แทน "ssl-root-cas / latest"
กฤษณะ

2

สิ่งนี้ใช้ได้ผลสำหรับฉัน => การเพิ่มตัวแทนและ 'ปฏิเสธไม่ได้รับอนุญาต' ตั้งค่าเป็นเท็จ

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


1

อีกวิธีหนึ่งในการแก้ปัญหานี้คือการใช้โมดูลต่อไปนี้

node_extra_ca_certs_mozilla_bundle

โมดูลนี้สามารถทำงานได้โดยไม่ต้องแก้ไขโค้ดใด ๆ โดยสร้างไฟล์ PEM ที่มีใบรับรองระดับรูทและระดับกลางทั้งหมดที่ Mozilla ไว้วางใจ คุณสามารถใช้ตัวแปรสภาพแวดล้อมต่อไปนี้ (ใช้ได้กับ Nodejs v7.3 +)

NODE_EXTRA_CA_CERTS

เพื่อสร้างไฟล์ PEM เพื่อใช้กับตัวแปรสภาพแวดล้อมข้างต้น คุณสามารถติดตั้งโมดูลโดยใช้:

npm install --save node_extra_ca_certs_mozilla_bundle

จากนั้นเรียกใช้สคริปต์โหนดของคุณด้วยตัวแปรสภาพแวดล้อม

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

วิธีอื่น ๆ ในการใช้ไฟล์ PEM ที่สร้างขึ้นมีอยู่ที่:

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

หมายเหตุ: ฉันเป็นผู้เขียนโมดูลข้างต้น


-3

ฉันใช้โมดูล nodemailer npm รหัสด้านล่างช่วยแก้ปัญหาได้

     tls: {
     // do not fail on invalid certs
     rejectUnauthorized: false
     }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.