Ошибка: невозможно проверить первый сертификат в nodejs
Я пытаюсь загрузить файл с сервера 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);
});
});
22 ответа
Попробуйте добавить соответствующий корневой сертификат
Это всегда будет намного более безопасным вариантом, чем просто слепое принятие несанкционированных конечных точек, которые, в свою очередь, должны использоваться только в качестве крайней меры.
Это может быть так просто, как добавление
require('https').globalAgent.options.ca = require('ssl-root-cas/latest').create();
к вашему заявлению.
Пакет SSL корневых центров сертификации (как используется здесь) является очень полезным пакетом для решения этой проблемы.
unable to verify the first certificate
The certificate chain is incomplete.
It means that the webserver you are connecting to is misconfigured and did not include the intermediate certificate in the certificate chain it sent to you.
Certificate chain
It most likely looks as follows:
- Server certificate - stores a certificate signed by intermediate.
- Intermediate certificate - stores a certificate signed by root.
- Root certificate - stores a self-signed certificate.
Intermediate certificate should be installed on the server, along with the server certificate.
Root certificates are embedded into the software applications, browsers and operating systems.
The application serving the certificate has to send the complete chain, this means the server certificate itself and all the intermediates. The root certificate is supposed to be known by the client.
Recreate the problem
Go to https://incomplete-chain.badssl.com/ using your browser.
It doesn't show any error (padlock in the address bar is green).
It's because browsers tend to complete the chain if it’s not sent from the server.
Now, connect to https://incomplete-chain.badssl.com/ using 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: "Error: unable to verify the first certificate".
Solution
You need to complete the certificate chain yourself.
To do that:
1: You need to get the missing intermediate certificate in .pem
format, then
2a: extend Node’s built-in certificate store using NODE_EXTRA_CA_CERTS
,
2b: or pass your own certificate bundle (intermediates and root) using ca
option.
1. How do I get intermediate certificate?
Using openssl
(comes with Git for Windows).
Save the remote server's certificate details:
openssl s_client -connect incomplete-chain.badssl.com:443 -servername incomplete-chain.badssl.com | tee logcertfile
We're looking for the issuer (the intermediate certificate is the issuer / signer of the server certificate):
openssl x509 -in logcertfile -noout -text | grep -i "issuer"
It should give you URI of the signing certificate. Download it:
curl --output intermediate.crt http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt
Finally, convert it to .pem
:
openssl x509 -inform DER -in intermediate.crt -out intermediate.pem -text
2a. NODE_EXTRA_CERTS
I'm using cross-env to set environment variables in package.json
file:
"start": "cross-env NODE_EXTRA_CA_CERTS=\"C:\\Users\\USERNAME\\Desktop\\ssl-connect\\intermediate.pem\" node index.js"
2b. ca
option
This option is going to overwrite the Node's built-in root CAs.
That's why we need to create our own root CA. Use ssl-root-cas.
Then, create a custom https
agent configured with our certificate bundle (root and intermediate). Pass this agent to axios
when making request.
// 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);
});
Instead of creating a custom https
agent and passing it to axios
, you can place the certifcates on the https
global agent:
// Applies to ALL requests (whether using https directly or the request module)
https.globalAgent.options.ca = rootCas;
Resources:
- https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded
- https://www.npmjs.com/package/ssl-root-cas
- https://github.com/nodejs/node/issues/16336
- https://www.namecheap.com/support/knowledgebase/article.aspx/9605/69/how-to-check-ca-chain-installation
- https://superuser.com/questions/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file/
- How to convert.crt to.pem
Еще один грязный хак, который сделает все ваши запросы небезопасными:
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/
Посмотрите на эту ошибку:
Цепочка сертификатов этого сервера неполная.
И это:
Проблемы с цепочкой.........
Установите это в dev env:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
Это фактически решило это для меня, из 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
});
Другой способ решить эту проблему - использовать следующий модуль.
https://www.npmjs.com/package/node_extra_ca_certs_mozilla_bundle
Этот модуль может работать без каких-либо изменений кода, создавая файл PEM, который включает все корневые и промежуточные сертификаты, которым доверяет Mozilla. Вы можете использовать следующую переменную среды (работает с Nodejs v7.3+),
Чтобы создать файл 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
ПРИМЕЧАНИЕ: я являюсь автором вышеуказанного модуля.
Вы можете сделать это, изменив параметры запроса, как показано ниже. Если вы используете самозаверяющий сертификат или пропавшего посредника, установка false для значение SSSS не заставит пакет запроса проверять сертификат.
var options = {
host: 'jira.example.com',
path: '/secure/attachment/206906/update.xlsx',
strictSSL: false
}
Вы можете отключить проверку сертификатов глобально - независимо от того, какой пакет вы используете для выполнения запросов - например:
// Disable certificate errors globally
// (ES6 imports (eg typescript))
//
import * as https from 'https'
https.globalAgent.options.rejectUnauthorized = false
Или же
// Disable certificate errors globally
// (vanilla nodejs)
//
require('https').globalAgent.options.rejectUnauthorized = false
Конечно, вам не следует этого делать, но это, безусловно, удобно для отладки и / или очень простых сценариев, когда вам абсолютно не важна правильная проверка сертификатов.
Сертификат GoDaddy SSL
Я испытал это, пытаясь подключиться к нашему внутреннему 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
Это сработало для меня => добавление агента и 'rejectUnauthorized' установлен в 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;
});
};
Ответ, предоставленный @sch, очень помог мне. Есть несколько дополнений, которые я хотел бы добавить. При работе в среде разработки, где ваш SSL-сертификат выдается одним из ваших собственных самозаверяющих сертификатов (поэтому нет промежуточного сертификата), именно на этот самозаверяющий сертификат необходимо ссылатьсяNODE_EXTRA_CA_CERTS
переменная окружения. Самоподписанный сертификат необходимо сохранить в формате PEM. Я сделал следующее, как только экспортировал свой сертификат:
set NODE_EXTRA_CA_CERTS=C:\rootCert.pem
(Стоит отметить, что я запускаю node в Windows, и путь к PEM не указан в кавычках)
Используя {{node}} из командной строки, я смог подтвердить, решил ли я проблему, вызвав:
https.get("https://my.dev-domain.local")
Я встречал очень редкий случай, но надеюсь, что это может кому-то помочь: сделал прокси-сервис, который проксировал запросы к другому сервису. И каждая ошибка запроса заключалась в том, что «не удалось проверить первый сертификат», даже когда я добавил все ожидаемые сертификаты.
Причина была довольно простой - я случайно повторно отправил также заголовок "host". Просто убедитесь, что вы не отправляете заголовок "host" явно.
Я столкнулся с этой проблемой несколько дней назад, и я придерживался этого подхода, и он работает для меня.
Для меня это происходило, когда я пытался получить данные с помощью axios или библиотек fetch, поскольку я нахожусь под корпоративным брандмауэром, поэтому у нас были определенные сертификаты, на которые хранилище сертификатов node js не могло указывать.
Итак, для моего loclahost я последовал этому подходу. Я создал папку в своем проекте и сохранил всю цепочку сертификатов в этой папке, а в моих сценариях для dev-server(package.json) я добавил это вместе с серверным скриптом, чтобы node js мог ссылаться на путь.
"dev-server":set NODE_EXTRA_CA_CERTS=certificates/certs-bundle.crt
Для своих серверов (разные среды) я создал новую переменную среды, как показано ниже, и добавил ее. Я использовал Openshift, но полагаю, что концепция будет такой же и для других.
"name":NODE_EXTRA_CA_CERTS
"value":certificates/certs-bundle.crt
В моем случае я не генерировал никаких сертификатов, так как вся цепочка сертификатов была мне уже доступна.
Мне удалось получить цепочку сертификатов через браузеры, такие как Mozilla или Chrome.
- откройте веб-сайт, перейдите к настройкам сертификата веб-страницы и загрузите цепочку сертификатов в виде имен файлов (first-chain.pem, second-chain.pem), должны быть в формате pem, например
----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB ...... -----END CERTIFICATE----- ----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB ...... -----END CERTIFICATE-----
- затем в вашем коде nodejs я сделал это на машинописном тексте, я добавил 2 cas, так как у меня есть 2 запроса веб-сервера
import https from 'https' import cas from 'ssl-root-cas'
......
interface CaList extends Buffer { addFile(file: string): Buffer[] } const caList = cas.create() as CaList caList.addFile(process.env.PROJECT_PATH + 'certs/first-chain.pem') caList.addFile(process.env.PROJECT_PATH + 'certs/second-chain.pem')
затем, когда мне нужно установить соединение wss с веб-сокетом, я добавляю агент со списком новых cas в запросы
this.client.connect(KtUrl, undefined, undefined, undefined, { agent: new https.Agent({ ca: caList }) })
также пришлось добавить файл определения для ssl-root-cas filename ssl-root-cas.d.ts, чтобы машинописный текст не жаловался
declare module 'ssl-root-cas' { function create(): string | Buffer | (string | Buffer)[] | undefined }
Вы используете, чтобы отправить запрос и получить эту ошибку?
Если это так, считайте, что ошибка
unable to verify the first certificate
может исходить от , не связанного с сервером. Чтобы решить эту проблему, вы должны настроить (или другое приложение для отправки запросов), чтобы разрешить несанкционированные запросы. Добавить
https.Agent
устанавливать
rejectUnauthorized: false
в конфиге запроса:
import axios from "axios"
import https from "https"
const getCities = async () => {
try {
const result = await axios.get("https://your-site/api/v1/get-cities", {
httpsAgent: new https.Agent({
rejectUnauthorized: false // set to false
})
})
console.log(result.data)
} catch(err) {
console.log(err?.message||err)
}
}
Если вы используете пользовательский
axios
экземпляр тогда:
import axios from "axios"
import https from "https"
export const request = axios.create({
baseURL: process.env.BASE_URL,
headers: {
Authorization: cookies.YOUR_ACCESS_TOKEN,
},
httpsAgent: new https.Agent({
rejectUnauthorized: false //set to false
})
})
У меня была эта проблема в Flask, когда третья сторона звонила на мой веб-сервер HTTPS, легко воссоздаваемая с помощью Postman, который показывает сообщение
ошибка: невозможно проверить первый сертификат
или через
openssl s_client -connect www.rc8.net:443 -servername www.rc8.com | tee logcertfile
после многих поисков и попыток разных решений я нашел решение, позволяет encrpyt выдавать ошибку
используйте это в опции Flask Run -cert, а не в cert.pem
Надеюсь, это сэкономит кому-нибудь время
Запрос Axios : основной причиной этой проблемы является то, что ваш код не может обрабатывать управление сертификатами. Чтобы решить эту проблему, добавьте приведенный ниже код.
import * as https from "https";
...
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
// А теперь передаем httpsAgent в запрос axios. Как показано ниже.
const { data } = await axios.get(url, { httpsAgent });
Мы предоставляем действительныеRoot.pem and Intermediate.pem
сертификат вagentOptions
свойство объекта запроса
бывший:
agentOptions: {
ca: [
fs.readFileSync("./ROOT.pem"),
fs.readFileSync("./Intermediate.pem"),
],
},
для получения дополнительной информации: https://stackoverflow.com/a/72582263/4652706
ЭТО РАБОТАЛО ДЛЯ МЕНЯ
Сделайте следующее
Если у вас нет этих пакетов https и axios
Вы можете установить с помощью npm install --save axios https
import axios from 'axios';
import https from 'https';
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
})
axios.defaults.httpsAgent = httpsAgent
Бум, сделав это, вы получите ответ ур.
I was using nodemailer npm module. Приведенный ниже код решил проблему
tls: {
// do not fail on invalid certs
rejectUnauthorized: false
}