How to write a proper HTTPS header? - node.js

I've built a NodeJS TCP server that sits on secured port 443.
Instead of sending HTTP snippets, I am using 443 to send my custom data through.
It all works ok, until someone tries to navigate to the https address of node server.
Like to ask how do I send a 404 Not Found HTTPS response if someone were to navigate to the https address of my node server?
E.g:
if( isHTTPRequest ) {
socket.write("HTTP/1.1 404 Not Found");
return;
}
Above doesn't work.

There's nothing magic about port 443; it doesn't intrinsically make things secure. It's functionally the same as port 442, 444, 80, or any other. The only thing that's special about 443 is that by convention, HTTPS servers listen on that port.
The thing that makes HTTPS servers secure is that they implement the TLS protocol. TLS is a cryptographic protocol that provides an application a secure communication channel. TLS provides the security; the server then runs regular HTTP on top of the TLS channel.
TLS isn't just limited to HTTP servers. Any application can use it; its high-level APIs provide a data stream that works just like a regular TCP stream.
Fortunately, node includes a complete TLS implementation that is almost entirely API-compatible with a regular TCP stream provided by the net module. So instead of:
net.createServer(function(socket) {
socket.write('stuff that gets sent in the clear');
});
Do this:
tls.createServer({
cert: fs.readFileSync('cert.pem'), // the pem-encoded certificate
key: fs.readFileSync('key.pem'), // the private key that goes with your cert
ca: [] // you may need to include the CA's intermediate certs if applicable
}, function(socket) {
socket.write('stuff that will be encrypted');
});
Likewise, clients use tls.connect() instead of net.connect().
You generate your private key using OpenSSL, then submit a CSR to a Certificate Authority, who will give you a certificate that will be broadly accepted. You can also self-sign, but self-signed certificates won't be accepted by clients by default.
Finally, given that you're writing a custom server (not a HTTP server), you shouldn't be listening on port 443. Convention says that HTTPS servers listen on port 443, and your custom protocol isn't HTTP. Select a different port, preferably one that isn't already assigned.

You could let openssl client do the hard part. Check out their documentation and examples.

Related

Is SSL secure connection available without browser call?

I have a question about SSL. As I know, when we use browser to request from https server, it will make an SSL handshake first then all data will be encryption in the connection. But if I make a request without browser (like request module in nodejs, postman...), will it be an SSL handshake and data encryption on the connection?
Anyone know please explain to me, thank you.
First, stop saying SSL. Its successor is TLS, and it will have 20 years next January.
TLS is a protocol sitting on top of TCP typically (other variants can also use UDP), and provides on top of TCP features some new features about endpoints authentication and transport confidentiality and integrity.
In a way, you can understand it as being sandwiched between TCP and the higher level application protocol, like HTTP.
Saying otherwise you can use many others protocols on top of TLS: you have all email related ones (SMTP, IMAP, POP, etc.), you can have FTP on top of it (while probably not a good idea nowadays), XMPP for realtime communications, etc.
In short, any protocol using TCP could use TLS with some adaptation.
So HTTP is one case among others. HTTP is between an HTTP client and an HTTP server, or webserver for short.
A browser is an HTTP client. One among many ones. When you use curl or wget you are also an HTTP client. So if any HTTP client access an http:// link it will first do the TLS handshake, after the TCP connection and before starting to do anything really related to the HTTP protocol.
You have specialized libraries dealing with TLS so that not all program need to recode everything about this again, since it is also complicated.

require('https') vs require('tls')

I'm trying to create a very secure connection between client and server using Node.js, Express.js and TLS (1.2).
I think my problem is in understanding what TLS actually is - meaning what is being exchanged, when and how by who.
Anyhow, I'm searching the internet like a nutter (crazy person) to try and figure out following:
what does var tls = require('tls'); invoke?
what does var https = require('https'); invoke?
I can get tls working when using another node as a client, but in this case the client will be a user in a browser. Can I use both for a browser or only https??
Thanks
Let's indeed start with what TLS is.
TLS is a way to provide secure connections between a client and a server. It does this by providing a safe way for clients and servers to exchange keys so they can then use public-key cryptography to secure their transmission. The exact mechanism is found here, but it's really not important for this answer.
Now, what is https? Well first, let's talk about HTTP. HTTP is a protocol that defines how web servers and clients talk and exchange web pages or data. Basically, it includes a request from a client and the server responds with a numerical message, headers, and (optionally) a body. If you're familiar with writing web pages, this is obvious.
So now, finally, what is HTTPS? HTTPS is a version of HTTP using TLS to secure data. This means that clients and servers can use the same protocol they're used to, wrapped in encryption.
Now, let's talk about these in node.js.
When you use require('tls'), you're only using the encryption layer, without defining the protocol. This will work fine for anything that doesn't expect an exact protocol, such as your other node.js client.
When you use require('https'), you're specifically using HTTP over TLS. The https module is actually a subclass of the tls module! (Oops, actually, the https.Server is a subclass of tls.Server) This means that whenever you're using the https module, you're also using the tls one.
Now, the final question: What does the browser want? If you've been following everything I've said, you can see that the browser wants https. In fact, it's likely that most of the webpages you've visited today has been over https.

Create a Reverse Proxy in NodeJS that can handle multiple secure domains

I'm trying to create a reverse proxy in NodeJS. But I keep running the issue that in that I can only serve one one set of cert/key pair on the same port(443), even though I want to serve multiple domains. I have done the research and keep running into teh same road block:
A node script that can serve multiple domains secure domain from non-secure local source (http local accessed and served https public)
Let me dynamically server SSL certificates via domain header
Example:
https ://www.someplace.com:443 will pull from http ://thisipaddress:8000 and use the cert and key files for www.someplace.com
https ://www.anotherplace.com:443 will pull from http ://thisipaddress:8080 and use the cert and key files for www.anotherplace.com
ect.
I have looked at using NodeJS's https.createServer(options, [requestListener])
But this method supports just one cert/key pair per port
I can't find a way to dynamically switch certs based on domain header
I can't ask my people to use custom https ports
And I'll run into browse SSL certificate error if I serve the same SSL certificate for multiple domain names, even if it is secure
I looked at node-http-proxy but as far as I can see it has the same limitations
I looked into Apache mod-proxy and nginx but I would rather have something I have more direct control of
If anyone can show me an example of serving multiple secure domains each with their own certificate from the same port number (443) using NodeJS and either https.createServer or node-http-proxy I would be indebted to you.
Let me dynamically server SSL certificates via domain header
There is no domain header so I guess you mean the Host header in the HTTP request.
But, this will not work because
HTTPS is HTTP encapsulated inside SSL
therefore you first have to do your SSL layer (e.g. SSL handshake, which requires the certificates), then comes the HTTP layer
but the Host header is inside the HTTP layer :(
In former times you would need to have a single IP address for each SSL certificate. Current browsers do support SNI (server name indication), which sends the expected target host already inside the SSL layer. It looks like node.js does support this, look for SNICallback.
But, beware that there are still enough libraries out there, which either don't support SNI on the client side at all or where one needs to use it explicitly. But, as long you only want to support browsers this should be ok.
Redbird actually does this very gracefully and not too hard to configure either.
https://github.com/OptimalBits/redbird
Here is the solution you might be looking at,
I found it very useful for my implementation
though you will need to do huge customization to handle domains
node-http-rev proxy:
https://github.com/nodejitsu/node-http-proxy
Bouncy is a good library to do this and has an example of what you are needing.
As Steffen Ullrich says it will depend on the browser support for it
How about creating the SSL servers on different ports and using node-http-proxy as a server on 443 to relay the request based on domain.
You stated you don't want to use nginx for that, and I don't understand why. You can just setup multiple locations for your nginx. Have each of them listen to different hostnames and all on port 443.
Give all of them a proxypass to your nodejs server. To my understanding, that serves all of your requirements and is state of the art.

WebSocket connection failed in mobile network on port 8080 or 80

In my web application I use Node.js and Socket.io.
As I testet the application local or in my wifi network everything was fine but since I switched to mobile data I get the connection error:
WebSocket connection to
'ws://url/socket.io/1/websocket/'
failed: Error during WebSocket handshake: Unexpected response code:
502
It seems that my mobile provider is blocking the ports 8080 and 80 but switching to another port seems not possible with nodejitsu.
Has someone some experience how to handle mobile network, node.js and socket.io?
EDIT:
As host I tried a free subscription of Heroku and a free subscription of Nodejitsu.
On mobile I checked the ports on http://websocketstest.com/ with mobile and wifi. On mobile the test failed with the ports 80 and 8080.
I thought maybe I could change the port to 443 but nodejitsu seems to listen to port 80 even if I set server.listen() to port 443.
I think WebSockets could be possible maybe via another port but I don't know how to change the port or why nodejitsu for example is just listening to port 80.
HTTP 502 means "Bad Gateway" and usually the sign of a reverse proxy somewhere along the path between client and server.
If you're sure it's not because of your own setup, you should try to disable the websocket (and flashsocket) transports for socket.io:
var io = require('socket.io').listen(...);
io.set('transports', [ 'xhr-polling', 'jsonp-polling', 'htmlfile' ]);
Your node.js app uses websockets and works locally and over a wifi network. When using mobile data network it fails with a 502. This is the question I'm answering not the heroku/nodejitsu edit.
The most probable reason for this is a bad hop in the network e.g. a reverse proxy. To use Websockets through a reverse proxy you must use http1.1 and upgrade on connect to go from http to ws protocols. Some proxies do not (yet) support http upgrade, some other things (e.g. #roberklep Opera Turbo) mangle that so websockets don't work.
Moving from http on port 80 to https on port 443 (using SSL which is now known as TLS) encrypts the data at the transport layer. Most of those bad hops are set up to send that data straight through which clears up your problem.
I found another StackOverflow Q&A showing some code setting up https in node.js
The key bits of that post being:
var sslOptions = {
key: fs.readFileSync(__dirname + '/../ssl/server.key,
cert: fs.readFileSync(__dirname + '/../ssl/server.pem,
ciphers: 'ECDHE-RSA-AES256-SHA:AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM',
honorCipherOrder: true };
and
var server = https.createServer(sslOptions, app);
server.listen(443);
I also found a useful introduction to SSL/TLS that includes instructions on how to get a free certificate.
You must set data transport configuration:
var io= require('socket.io');
io.set("transports", ["xhr-polling"]);

Difference between HTTPS and SSL

What is the difference between HTTPS and SSL? I read about them and found following:
HTTPS: HTTPS is a combination of HTTP with SSL/TLS. It means that HTTPS is basically HTTP connection which is delivering the data secured using SSL/TLS.
SSL: SSL is a secure protocol that works on the top of HTTP to provide security. That means SSL encrypted data will be routed using protocols like HTTP for communication.
I am wondering where is the difference between these two? Or both are identical?
The explanation of SSL that you've found is wrong.
SSL (Secure Socket Layer) or TLS (Transport Layer Security) works on top of the transport layer, in your examples TCP. TLS can be used for more or less any protocol, HTTPS is just one common instance of it.
HTTP is an application layer protocol.
In regular, non-encrypted HTTP, the protocol stack can look like this:
HTTP
TCP
IP
Ethernet
When using HTTPS, the stack looks like this:
HTTP
TLS (SSL)
TCP
IP
Ethernet
HTTPS runs over SSL (as it's name suggests, HTTP-over-SSL), not SSL over HTTP. First SSL session is established, then all HTTP data are wrapped into secured SSL packets before sending and after receiving.
SSL (Secure Sockets Layer) is a standard security technology to create an encrypted link between a server and a client. This link ensures that all data passed between the server and the client remain private and secure.
It was designed to support protocols such as FTP, HTTP, TELNET.
Hypertext Transfer Protocol Secure (HTTPS) or “HTTP Secure,” is an application specific implementation that is a combination of the Hypertext Transfer Protocol (HTTP) with the SSL/TLS. HTTPS is used to provide encrypted communication and secure identification of a server, so that no middle man can intercept the data easily.
As everything in HTTP is in plain text (or encoded) , it is used with SSL/TLS to encrypt it.
Found this link which explains SSL, TLS, HTTPS :
http://nexsniper.blogspot.com/2017/11/what-is-ssl-tls-and-https.html

Resources