I have this Curl command that works fine.
curl -vvv "https://<URL>:<port> --cert /etc/identity/client/certificates/client.pem --key /etc/identity/client/keys/client-key.pem --cacert /etc/identity/ca/cacerts.pem --capath /etc/identity/client/certificates
But when i try to translate from Curl syntax to NodeJs native(https) i got this error:
Http is failed. error: Error: write EPROTO 140452300855232:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:332:
The nodeJs implementation looks like this:
Options:
options = {
hostname:'url',
path: 'path',
method: 'GET',
port: port,
cert: [fs.readFileSync('/etc/identity/client/certificates/client.pem'), {encoding: 'utf-8'}],
key: [fs.readFileSync('/etc/identity/client/keys/client-key.pem'), {encoding: 'utf-8'}],
ca: [fs.readFileSync('/etc/identity/ca/cacerts.pem'), {encoding: 'utf-8'}]
};
Request:
https.request(options, res => {...do something})
The only thing i can think about is that the Curl command has 4 parameters(cert, key, cacert, capath) and on the node.https library i can pass only 3 parameters.
Any thoughts/Ideas?
Thanks
Related
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
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;
With OpenSSL and adding CA have curl -X GET https://someserver.com -I --capath /etc/ssl/certs/ working fine. curl -X GET https://someserver.com -I --cacert /etc/ssl/certs/someserver.pem works as well.
I would like to make the same call with node.js https.request()
I've tried node --use-openssl-ca app.js with SSL_CERT_DIR set to /etc/ssl/certs/ - didn't help.
Tried:
const https = require('https');
const fs = require('fs');
const options = {
hostname: 'someserver.com',
port: 443,
path: '/',
method: 'GET',
ca: fs.readFileSync('/etc/ssl/certs/someserver.pem')
}
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
req.on('error', error => {
console.error(error)
})
req.end()
Error: write EPROTO 140249139050368:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1544:SSL alert number 40
Thank you.
Just rebuild node.js with openssl support. So now I use the same libs as curl.
Thank you everyone
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.
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;