I need to check if a given domain has a valid SSL certificate using NodeJS, so I'm using the https module like so:
const https = require('https');
const options = {
host: 'www.some-site.com',
method: 'get',
path: '/'
};
const req = https.request(options, res => {
console.log('Certificate Status: ', res.socket.authorized);
});
req.on('error', error => {
console.error('Error: ', error);
});
req.end();
I've been testing this against some of the sites listed as being insecure in Google's transparency report: https://transparencyreport.google.com/https/top-sites
Some of the results:
aliexpress.com - Certificate Status: true Correct
expired.badssl.com - Certificate Status: false Correct
Both correct. However, when a site does not use SSL at all the value for socket.authorized is still true:
alibaba.com - Certificate Status: true Wrong
www.bbc.com - Certificate Status: true Wrong
What is the correct way to validate whether a domain has a valid SSL certificate?
As pointed out in the comments for my question, these sites happened to be using redirects from https to http as soon as the sites loaded. So the initial 'valid cert' results were correct.
To get around this I used an npm package zfollow-redirects`, and this solved the issue.
From the package description:
Drop-in replacement for Nodes http and https that automatically
follows redirects.
Related
I'm having some trouble with the fetch and node.js.
In my frontend when i click a button, i would like to send a post request in order to receive an array from my backend as answer. I'n my backend i'm using node.js with express, in my frontend i'm using the fetch function.
The error that occours is the following:
Access to fetch at 'http://localhost:8080/api' from origin 'real localhost address' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Code Here
const getArray = async() => {
const data = await fetch ("http://localhost:8080/api");
const dataJson = await data.json();
console.log(dataJson)
}
getArray();
In my server i've got
app.post("/api", (req,res) => {
res.sendFile(JSON.stringify(arr));
});
You need to add request options. Please refer to the MDN docs for further information.
As #Kudah said, you should read the docs.
Fetch (and XMLHttpRequest) follow the same-origin policy. This means that browsers restrict cross-origin HTTP requests from within scripts. A cross-origin request occurs when one domain (for example http://example2.com/) requests a resource from a separate domain (for example http://example1.com/).
The easiest way to solve this, (If you don't want to dig too much into this)
const whiteList = [ "https://myRealBackendUrl-1", "https://myRealBackendUrl-2" ];
// you can also pass a string here instead here instead of array
const corsOptions = {
credentials: true,
origin: process.env.NODE_ENV !== production ? "http://localhost:4000" : whiteList
// if you are in a dev environment, you probably want something like localhost
// http://localhost:4000 is just a demo backend. replace it with your own.
// if you are in a production environment, for example heroku then your backend
// url will be something like http://example.herokuapp.com
// in that case `const whiteList = [ "http://example.herokuapp.com" ];`
};
app.use(cors(corsOptions));
The above code should be enough for the normal use case.
There is also callback function, it is if you want to run some function of your own. Don't read it if you dont plan to use any dynamic checking
var corsOptionsDelegate = async (req, callback) => {
var corsOptions = { origin: false };
try {
// you can do some dynamic check here
// For example: check database for some conditions then allow access
if( myDatabaseSays == true ) corsOptions.origin = true;
else corsOptions.origin = false;
} catch (err) {
console.log(err);
// corsOptions.origin = false;
}
callback(null, corsOptions) // chain it
}
Anyway read the docs properly for more info
[1]: https://expressjs.com/en/resources/middleware/cors.html
I am trying to fetch food by its key. In postman api is working fine but is the forntend it has no response.
backend code
app.get('/foods/:key', (req, res) => {
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
res.send(documents[0])
})
})
frontend code
const { key } = useParams()
const [foodById, setFoodById] = useState({})
useEffect(() => {
fetch(`http://localhost:5000/foods/${key}`)
.then((res) => res.json())
.then((data) => {
setFoodById(data)
})
}, [key])
Although you've added some images above, the most important is missing, namely, what are the Browser's Developer Tools stating the problem is. You should see some message in the Console tab, as well as in the Network tab for that particular request, if it is indeed being made. Until anyone sees this, it will be very difficult to help in fixing your problem.
If your not already, I suggest scaffolding any react app with create-react-app (CRA). This will give you a working app to start from. You can ignore CORS related issues in development, if using CRA, by adding "proxy": "http://localhost:5000", to your package.json file, see here for more on this method, but remember, this is only works for local development. You can also start Chrome to ignore Web Security by running it with the --disable-web-security flag e.g. chromium --disable-web-security, but that isn't a great idea really, more a way to quickly determine if you are having CORS problems, as Chrome masks some problems as CORS related, when in fact they aren't.
I'd also suggest changing your fetch code to use await, so instead you'd have:
const response = await fetch(`http://localhost:5000/foods/${key}`);
if (!response.ok) {
console.error(`Error message: ${response.statusText} ${response.status}`);
}
const result = response.json();
console.log(result);
This isn't necessary, but I've always found it way easier to read than the then/catch/finally method.
Reason for error
You need to stringify an object before sending it to the client with the JSON.stringify() method. When we exchange data to/from a web server, it must be a string.
Solution:
Proper way to send response to the client would to wrap the entire API in a try-catch block and explicitly specify the HTTP Status Code along with the stringified data in every response.
Note: Although 500 status code is used for error handling, you should choose one as per the use case.
app.get('/foods/:key', (req, res) => {
try {
/*
rest of the code
*/
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
if (err) {
// 500 stands for internal server error
return res.status(500).send(JSON.stringify('Here goes a meaningful error message!'));
}
// 200 stands for success
res.status(200).send(JSON.stringify(documents[0]));
});
/*
rest of the code
*/
} catch (error) {
// 500 stands for internal server error
res.status(500).send(JSON.stringify('Here goes another meaningful error message!'));
}
})
The problem is that you haven't set the CORS headers of response in your backend code. and you are using different ports in your backend and frontend (5000 & 3000) so the Same Origin Policy disallows reading the remote resource, indicating that the request was blocked due to violating the CORS security rules.
you've to set the CORS headers.
you can install the CORS npm package and follow it's instructions to resolve the issue like this:
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
.
.
.
And one other issue that I'm seeing is that you've put the react-router default route before your specified path. so move the <route path="*"> after <route path="/foods/:key">
I have an azure chat bot and I use it per direct line channel.
It is working fine if I use the secret directly in the HTML, but due to safety reasons I want to use Tokens. Thats why I used that:
<script>
window
.fetch('http://XXXXXXXX.azurewebsites.net/token-generate',
{
method: 'POST'
})
.then(function(res) {
return res.json();
})
.then(function(json) {
const token = json.token;
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({
token: token
})
},
document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
});
</script>
It is like that and not with an async function because it needs to work on IE11 too.
My index.js in my bot looks like this:
// Create HTTP server
const server = restify.createServer({
name: 'token-server'
});
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
server.post('/token-generate', async (_, res) => {
console.log('requesting token ');
res.setHeader('Access-Control-Allow-Origin', '*');
console.log(res);
try {
const cres = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
headers: {
authorization: `Bearer ${ process.env.DIRECT_LINE_SECRET }`
},
method: 'POST'
});
// console.log(cres);
const json = await cres.json();
// console.log(json);
// json.header.append('Access-Control-Allow-Origin: *');
console.log(json);
if ('error' in json) {
res.send(500);
} else {
res.send(json);
}
} catch (err) {
res.send(500);
}
});
That is some code I found after some research how to use tokens to render the Webchat.
My problem is, that when I use this html code, I get some Errors:
Access to fetch at 'http://compliancebotbbraun-bot.azurewebsites.net/token-generate' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
testbot.html:1 Uncaught (in promise) TypeError: Failed to fetch
and I just don't know how to change the Access-Control-Allow-Origin header. I don't find anything online and if I find something, it is not even close to my code.
It is working exactly as I tought it would work in IE11, but in Chrome,Edge and Firefox (idk for others, only tested these) these Errors are occuring.
I hope someone here can help me.
Based on my understanding , you exposed an API to grant access tokens to your bot clients by post method to your bot clients. Your bot clients use JS script to invoke this API . As you are using post method, so your bot clients will encounter CORS issues .
Based on the host of /token-generate url , this API is hosted on Azure webapp , you can just refer to this doc to define allowed domains to call this API from a static page by JS on Azure portal directly.
You can find the Azure webapp which hostes your API code here :
And open CORS settings here :
If you are just testing your bot from local static html file , adding "*" and remove other domains in CORS config will solve this issue .
Test result :
Hope it helps . If you have any further concerns , pls feel free to let me know .
I am using nodemailer module to send mail from my nodejs application.
I am getting Error: connect ETIMEDOUT xxx.xxx.xx.xxx:465. Can any one help me in solving this.
Here I am pasting my code.
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'my_mail_id#gmail.com',
pass: 'my_gmail_password'
}
});
console.log('created');
transporter.sendMail({
from: 'my_mail_id#gmail.com',
to: 'my_mail_id#gmail.com',
subject: 'hello world!',
text: 'hello world!'
});
This may be firewall problem. I faced similar problem in Ubuntu (Digital Ocean server). Tried to fix the issue for 3 days, tried using auth2 also, tried with inactive firewall using ufw inactive command, but no luck. Finally I checked Digital Ocean admin panel and created firewall for the droplet. Problem solved by enabling TCP inbound and outbound in firewall settings.
Have you looked at this answer.
It turns out that in order for Google to authorize a third party server to access your account via SMTP now, you have to enable “Less Secure Apps” on your gmail account, if you want to use username/password (more info here).
So you have two option:
use OAuth
make your account less secure
// Create a SMTP transport object
var transport = nodemailer.createTransport("SMTP", {
service: 'Hotmail',
auth: {
user: "username",
pass: "paasweord"
}
});
console.log('SMTP Configured');
// Message object
var message = {
// sender info
from: 'abc#hotmail.com',
// Comma separated list of recipients
to: req.query.to //'aadityashukla9#hotmail.com',
// Subject of the message
subject:req.query.subject //'Nodemailer is unicode friendly ✔',
// plaintext body
text: req.query.text //'Hello to myself!',
// HTML body
/* html:'<p><b>Hello</b> to myself <img src="cid:note#node"/></p>'+
'<p>Here\'s a nyan cat for you as an embedded attachment:<br/></p>'*/
};
console.log('Sending Mail');
transport.sendMail(message, function(error){
if(error){
console.log('Error occured');
console.log(error.message);
return;
}
console.log('Message sent successfully!');
//transport.close(); // close the connection pool
});
I experienced this same issue today, found this documentation...
https://nodemailer.com/usage/using-gmail/
Had to do a capcha process from the server, by visiting a url while logged into gmail.
Hopefully it helps others.
There are the only reasons of this error:
Less Secure Apps: you have to Enable the "Less Secure Apps" from your Gmail account.
Use OAuth
Besides the already mentioned reference to the information at https://nodemailer.com/usage/using-gmail/, in my case the Internet Router (Speedport W724V) was still a problem. This keeps a list of all allowed SMTP servers. After I had extended the list accordingly, it worked perfectly. I had to do the same with smtp.ethereal.email.
I'm not sure if should be posting this answer but I've faced the same problem while using GMAIL and the reason behind the error for me was being connected to a vpn. I disabled it and now it works.
I'm using an application password
open port inbound outbound rule 587 or others, whichever you are using on server aws/google etc.
host: host,
secureConnection: false,
port: 465,
secure: true,
auth: {
user: user,
pass: pass
}
This test program connects to an https server and gets some content. I've checked my server in browsers and with curl and the certificate is working correctly. If I run curl to grab data from the server it correctly complains about the certificate being unknown unless I pass it in with --cacert or turn security off with -k.
So the problem I am having is that although I think my client should be doing certificate authentication and I am telling it where the public certificate is, it just always works. If I remove the ca: option so it has no idea what the certificate is from the server then it silently works. I would like to catch the authentication error but I can't seem to do so.
var https = require('https');
var fs = require('fs');
function main() {
var data = '';
var get = https.get({
path: '/',
host: 'localhost',
port: 8000,
agent: false,
ca: [ fs.readFileSync('https_simple/cacert.pem') ]
}, function(x) {
x.setEncoding('utf8');
x.on('data', function(c) {data += c});
x.on('error', function(e) {
throw e;
});
x.on('end', function() {
console.log('Hai!. Here is the response:');
console.log(data);
});
});
get.on('error', function(e) {throw e});
get.end();
}
main();
In order to make this work I needed to upgrade to v0.7.8 (although any v0.7 should be fine) where the rejectUnauthorized functionality has been added to https.get
This combination of options is needed:
agent: false, // or you can supply your own agent, but if you don't you must set to false
rejectUnauthorized: true,
ca: [ fs.readFileSync('https_simple/cacert.pem') ]
Now if the authentication fails you will get an 'error' event and the request will not go ahead.
See the https.request documentation for details on making your own Agent
The bug fix was committed in this change: https://github.com/joyent/node/commit/f8c335d0
As per the documentation for https.request, the ca option of both https.get and https.request is an option from tls.connect. The documentation for the options to the tls.connect module function states:
ca: An array of strings or Buffers of trusted certificates. If this is
omitted several well known "root" CAs will be used, like VeriSign.
These are used to authorize connections.
Digging into the node.js source, the root certs used can be found here: https://github.com/joyent/node/blob/master/src/node_root_certs.h
So in short, with no authority cert provided as an option to https.get the tls module will attempt to authenticate the connection using the list of root certs anyway.
I do this in npm, using the request module. It goes like this:
var cacert = ... // in npm, this is a config setting
var request = require("request")
request.get({ url: "https://...",
ca: cacert,
strictSSL: true })
.on("response", function (resp) { ... })
.on("error", function (er) { ... })
The error event will be raised if the ssl isn't valid.
In V 0.6.15 you need to explicitly check whether or not the certificate validation passed or failed.
if (x.connection.authorized === false) {
console.log('SSL Authentication failed');
} else if (x.connection.authorized === true) {
console.log('SSL Authentication succeeded');
}