Nodejs https does not fail on ssl certificate failure - node.js

I am using the https library for nodejs to send a https get request using the following code. I get a valid 200 status even though the certificate of the server being tested is expired.
https.get(options, this.onResponseCallback.bind(this));
The value of options is shown below.
{
protocol: 'https: ',
slashes: true,
auth: null,
host: 'XXXXXXXX',
port: '443',
hostname: 'XXXXXXXX',
hash: null,
search: 'XXXXXXXX',
query: 'XXXXXXXX',
pathname: '/XXXXXXXX/XXXXXXXX',
path: '/XXXXXXXX/XXXXXXXX?XXXXXXXX',
href: 'https://XXXXXXXX',
headers: {
'User-Agent': 'NodeUptime/3.0(https://github.com/fzaninotto/uptime)'
},
rejectUnauthorized: true
}
If I hit the same URL in the browser I get the following error.
How do I get nodejs to fail when the cert is expired?

I think browser security policy is a bit stricter than what you can do in node.
You can access info about server's certificate by:
https.request(options, function(response){
var cert = response.client.pair.cleartext.getPeerCertificate();
});
.valid_to is what you are looking for.
More info about TLS.

Related

tunneling socket could not be established, EPROTO 139829749196736

I am facing the issue while calling Node js application from server, "Error: tunneling socket could not be established, cause=write EPROTO 139829749196736:error:1408F10B:SSL routines:ssl3_get_record:wrong version"
I used this code snippet below:
var options = {
'method': "POST",
'url': process.env.QATAPIPATH + process.env.APIPATH + parameter,
'headers': {
'Content-Type': 'application/json'
},
ignoreTLS: true,
secure: false,
body: JSON.stringify({
"data": req.body.data
})
};
The above SSL errors are thrown because the client is not able to verify the trust chain of the self-signed server certificate
The easiest solution to resolve these errors is to use the “rejectUnauthorized”
> https.request({
> ....,
> rejectUnauthorized: false,
> }, ...)
or set it as an environment variable
NODE_TLS_REJECT_UNAUTHORIZED=0
for Linux
export NODE_TLS_REJECT_UNAUTHORIZED=0

What is similar to -k / --insecure of CURL for Nodejs? [duplicate]

I'm working on a little app that logs into my local wireless router (Linksys) but I'm running into a problem with the router's self-signed ssl certificate.
I ran wget 192.168.1.1 and get:
ERROR: cannot verify 192.168.1.1's certificate, issued by `/C=US/ST=California/L=Irvine/O=Cisco-Linksys, LLC/OU=Division/CN=Linksys/emailAddress=support#linksys.com':
Self-signed certificate encountered.
ERROR: certificate common name `Linksys' doesn't match requested host name `192.168.1.1'.
To connect to 192.168.1.1 insecurely, use `--no-check-certificate'.
In node, the error being caught is:
{ [Error: socket hang up] code: 'ECONNRESET' }
My current sample code is:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
method: 'GET'
}, function(res){
var body = [];
res.on('data', function(data){
body.push(data);
});
res.on('end', function(){
console.log( body.join('') );
});
});
req.end();
req.on('error', function(err){
console.log(err);
});
How can I go about getting node.js to do the equivalent of "--no-check-certificate"?
Cheap and insecure answer:
Add
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
in code, before calling https.request()
A more secure way (the solution above makes the whole node process insecure) is answered in this question
In your request options, try including the following:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
method: 'GET',
rejectUnauthorized: false,
requestCert: true,
agent: false
},
Don't believe all those who try to mislead you.
In your request, just add:
ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})]
If you turn on unauthorized certificates, you will not be protected at all (exposed to MITM for not validating identity), and working without SSL won't be a big difference. The solution is to specify the CA certificate that you expect as shown in the next snippet. Make sure that the common name of the certificate is identical to the address you called in the request(As specified in the host):
What you will get then is:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})],
method: 'GET',
rejectUnauthorized: true,
requestCert: true,
agent: false
},
Please read this article (disclosure: blog post written by this answer's author) here in order to understand:
How CA Certificates work
How to generate CA Certs for testing easily in order to simulate production environment
Add the following environment variable:
NODE_TLS_REJECT_UNAUTHORIZED=0
e.g. with export:
export NODE_TLS_REJECT_UNAUTHORIZED=0
(with great thanks to Juanra)
Adding to #Armand answer:
Add the following environment variable:
NODE_TLS_REJECT_UNAUTHORIZED=0 e.g. with export:
export NODE_TLS_REJECT_UNAUTHORIZED=0 (with great thanks to Juanra)
If you on windows usage:
set NODE_TLS_REJECT_UNAUTHORIZED=0
Thanks to: #weagle08
You can also create a request instance with default options:
require('request').defaults({ rejectUnauthorized: false })
For meteorJS you can set with npmRequestOptions.
HTTP.post(url, {
npmRequestOptions: {
rejectUnauthorized: false // TODO remove when deploy
},
timeout: 30000, // 30s
data: xml
}, function(error, result) {
console.log('error: ' + error);
console.log('resultXml: ' + result);
});
try
export NODE_TLS_REJECT_UNAUTHORIZED=0
Or you can try to add in local name resolution (hosts file found in the directory etc in most operating systems, details differ) something like this:
192.168.1.1 Linksys
and next
var req = https.request({
host: 'Linksys',
port: 443,
path: '/',
method: 'GET'
...
will work.
So, my company just switched to Node.js v12.x.
I was using NODE_TLS_REJECT_UNAUTHORIZED, and it stopped working.
After some digging, I started using NODE_EXTRA_CA_CERTS=A_FILE_IN_OUR_PROJECT that has a PEM format of our self signed cert and all my scripts are working again.
So, if your project has self signed certs, perhaps this env var will help you.
Ref: https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file
In case you are looking for posting using #nestjs/axios,
here is the syntax without certificate (Non Production Solution):
const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
};
const responseData = await firstValueFrom(
this.httpService.post(url, data, config).pipe(map((response) => response.data)),
);
here is the syntax with certificate (Production Solution):
const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
httpsAgent: new https.Agent({
rejectUnauthorized: true,
ca: fs.readFileSync(path.join(__dirname, './resources/certificateName'))
}),
};
const responseData = await firstValueFrom(
this.httpService.post(url, data, config).pipe(map((response) => response.data)),
);
When you cannot control the request creation
When using packages you sometimes don't have the option to set the correct settings on the request call, nor does the package offer you a way to inject a request.
However you might still want to avoid the insecure NODE_TLS_REJECT_UNAUTHORIZED=0 and opt for only having an insecure connection to a specified target.
This is how I solved the issue:
// check if host and port fit your application
function isSelf(host, port) {
return host === myHost && port === myPort;
}
// get the built in tls module and overwrite the default connect behavior
const tls = require("tls");
const _connect = tls.connect;
function wrappedConnect(options, secureConnectListener) {
if (isSelf(options.host, options.port)) {
options.rejectUnauthorized = false;
}
return _connect(options, secureConnectListener);
}
tls.connect = wrappedConnect;

How to download a resource using https behind a proxy without using "rejectUnauthorized: false"?

I'm trying to download https://www.stackoverflow.com or https://www.google.com using got while I'm behind a proxy
I keep running into this error RequestError: unable to get local issue if rejectUnauthorized: false is not used. I know that this rejectUnauthorized: false workaround is a security issue.
stackoverflow.com and google.com must have trusted well-known CAs, so why am I getting this error?
import got from "got";
import { HttpsProxyAgent } from "hpagent";
const result = await got("https://www.google.com", {
agent: {
https: new HttpsProxyAgent({
proxy: process.env.https_proxy,
rejectUnauthorized: false, // If true => RequestError: unable to get local issuer certificate
}),
},
}).text();
console.log("result:", result);
On the other hand, this request to https://jsonplaceholder.typicode.com works without setting rejectUnauthorized: false
const result = await got("https://jsonplaceholder.typicode.com", {
agent: {
https: new HttpsProxyAgent({
proxy: process.env.https_proxy,
}),
},
}).text();
Can you please explain this inconsistency and how to resolve it?
Note: I'm using Node.js 14.17.6

Nest js - httpservice add proxy

I have the next proxy configuration (I just tested it in postman and its working)
host: 'http://tsl.proxyurl.com/standard.pac', port: 8080, protocol: https, no username, no password.
And this is my code:
const options = {
data,
'proxy': {
host: 'http://tsl.proxyurl.com/standard.pac',
port: 8080
},
'headers': {
'Authorization': `Basic ${base64Auth}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
};
const endpoint = `https://api.myrurl.com/st/token`;
const response = await this.httpService.post(endpoint, data, options).toPromise();
But every time, I try, I get:
message:'Client network socket disconnected before secure TLS
connection was established'
Someone had a similar issue? How can I fix it?

Ignore invalid self-signed ssl certificate in node.js with https.request?

I'm working on a little app that logs into my local wireless router (Linksys) but I'm running into a problem with the router's self-signed ssl certificate.
I ran wget 192.168.1.1 and get:
ERROR: cannot verify 192.168.1.1's certificate, issued by `/C=US/ST=California/L=Irvine/O=Cisco-Linksys, LLC/OU=Division/CN=Linksys/emailAddress=support#linksys.com':
Self-signed certificate encountered.
ERROR: certificate common name `Linksys' doesn't match requested host name `192.168.1.1'.
To connect to 192.168.1.1 insecurely, use `--no-check-certificate'.
In node, the error being caught is:
{ [Error: socket hang up] code: 'ECONNRESET' }
My current sample code is:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
method: 'GET'
}, function(res){
var body = [];
res.on('data', function(data){
body.push(data);
});
res.on('end', function(){
console.log( body.join('') );
});
});
req.end();
req.on('error', function(err){
console.log(err);
});
How can I go about getting node.js to do the equivalent of "--no-check-certificate"?
Cheap and insecure answer:
Add
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
in code, before calling https.request()
A more secure way (the solution above makes the whole node process insecure) is answered in this question
In your request options, try including the following:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
method: 'GET',
rejectUnauthorized: false,
requestCert: true,
agent: false
},
Don't believe all those who try to mislead you.
In your request, just add:
ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})]
If you turn on unauthorized certificates, you will not be protected at all (exposed to MITM for not validating identity), and working without SSL won't be a big difference. The solution is to specify the CA certificate that you expect as shown in the next snippet. Make sure that the common name of the certificate is identical to the address you called in the request(As specified in the host):
What you will get then is:
var req = https.request({
host: '192.168.1.1',
port: 443,
path: '/',
ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})],
method: 'GET',
rejectUnauthorized: true,
requestCert: true,
agent: false
},
Please read this article (disclosure: blog post written by this answer's author) here in order to understand:
How CA Certificates work
How to generate CA Certs for testing easily in order to simulate production environment
Add the following environment variable:
NODE_TLS_REJECT_UNAUTHORIZED=0
e.g. with export:
export NODE_TLS_REJECT_UNAUTHORIZED=0
(with great thanks to Juanra)
Adding to #Armand answer:
Add the following environment variable:
NODE_TLS_REJECT_UNAUTHORIZED=0 e.g. with export:
export NODE_TLS_REJECT_UNAUTHORIZED=0 (with great thanks to Juanra)
If you on windows usage:
set NODE_TLS_REJECT_UNAUTHORIZED=0
Thanks to: #weagle08
You can also create a request instance with default options:
require('request').defaults({ rejectUnauthorized: false })
For meteorJS you can set with npmRequestOptions.
HTTP.post(url, {
npmRequestOptions: {
rejectUnauthorized: false // TODO remove when deploy
},
timeout: 30000, // 30s
data: xml
}, function(error, result) {
console.log('error: ' + error);
console.log('resultXml: ' + result);
});
try
export NODE_TLS_REJECT_UNAUTHORIZED=0
Or you can try to add in local name resolution (hosts file found in the directory etc in most operating systems, details differ) something like this:
192.168.1.1 Linksys
and next
var req = https.request({
host: 'Linksys',
port: 443,
path: '/',
method: 'GET'
...
will work.
So, my company just switched to Node.js v12.x.
I was using NODE_TLS_REJECT_UNAUTHORIZED, and it stopped working.
After some digging, I started using NODE_EXTRA_CA_CERTS=A_FILE_IN_OUR_PROJECT that has a PEM format of our self signed cert and all my scripts are working again.
So, if your project has self signed certs, perhaps this env var will help you.
Ref: https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file
In case you are looking for posting using #nestjs/axios,
here is the syntax without certificate (Non Production Solution):
const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
httpsAgent: new https.Agent({
rejectUnauthorized: false
}),
};
const responseData = await firstValueFrom(
this.httpService.post(url, data, config).pipe(map((response) => response.data)),
);
here is the syntax with certificate (Production Solution):
const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
httpsAgent: new https.Agent({
rejectUnauthorized: true,
ca: fs.readFileSync(path.join(__dirname, './resources/certificateName'))
}),
};
const responseData = await firstValueFrom(
this.httpService.post(url, data, config).pipe(map((response) => response.data)),
);
When you cannot control the request creation
When using packages you sometimes don't have the option to set the correct settings on the request call, nor does the package offer you a way to inject a request.
However you might still want to avoid the insecure NODE_TLS_REJECT_UNAUTHORIZED=0 and opt for only having an insecure connection to a specified target.
This is how I solved the issue:
// check if host and port fit your application
function isSelf(host, port) {
return host === myHost && port === myPort;
}
// get the built in tls module and overwrite the default connect behavior
const tls = require("tls");
const _connect = tls.connect;
function wrappedConnect(options, secureConnectListener) {
if (isSelf(options.host, options.port)) {
options.rejectUnauthorized = false;
}
return _connect(options, secureConnectListener);
}
tls.connect = wrappedConnect;

Resources