Bootcamp Notes – Day 13 (Mon) – NodeJS: Week 4: Backend Services – HTTPS and Secure Communication
Backend Services – HTTPS and Secure Communication
Overview
This week will dive deeper into back-end concepts such as authentication and security issues. As a back end developer, the security of the server data and data sent from clients is paramount. You will be learning some basics of cryptography, including the concepts of: Symmetric-Key and public-key cryptography. We will also learn about the (TLS) Transport Layer Security protocol and how it uses SSL certificates, and will learn how to generate your own public & private key pairs. We also learn about how to set up an HTTPs server, that takes advantage of TLS to establish a secure channel for client server communication.
After this we will learn about how to enable file uploads on the server with a popular middleware called Multer. Then we will learn about the same-origin policy of browsers that exist for security purposes and how to circumvent it, when you need to using:
Cross-Origin Resource Sharing (CORS) & cors middleware
With help from the course middleware module for node and express.
Will learn also about the OAuth protocol and how it can be used for third-party authentication. Then you will actually register your application as a developer with Facebook, so you can enable logging into your server via it’s authentication service with the help of Passport.
HTTPS and Secure Communication
AUTHENTICATION
We learned a lot about authentication last week.
- User sends credentials through an HTPP client to a server in request body/header
- Server verifies credentials then generates a cookie or token
- Cookie is returned to client for further re-authorization for a period of time
In a real world situation you would need to add another element to the scenario, a secure channel.
HTTP is an insecure channel, and messages sent over it can be intercepted. So the user credentials could be captured by a third party.
The secure version of HTTP is called HTTPS. The S stands for secure. What makes it secure? To come to a general understanding of that, let us start with an overview of cryptography. Let us talk about a concept called…
SYMMETRIC-KEY CRYPTOGRAPHY
Let us say that you want to send a message over a network and want to encrypt the message in such a way that only the receiver will be able to decrypt it. That means both the sender and the receiver should establish between the two, how the encryption and decryption will work.
In symmetric-key cryptography, the sender and the receiver share a secret key known to both sides. Whenever the sender wants to send a message, the sender encrypts the message using an encryption algorithm, which uses the secret key as the other input to the algorithm. That means it applies a series of steps to the message that will transform it and scramble it to look completely different. Then the encrypted message will be sent out across the communication channel such as the internet to the receiver. The receiver then applies a corresponding decryption algorithm and it must apply the same secret key in order to decrypt it correctly. If a malicious third party is able to intercept this encrypted message in transit even if they were able to figure out the algorithm that was used, it would be extremely difficult to decrypt the message without the secret key. But, with brute force techniques, by just trying different keys over and over again eventually it would be possible to decrypt the message, but it would take a long time and require a lot of computation power. While encrypting a message with a secret key doesn’t mean it’s impossible to decrypt, it makes it very difficult.
The drawback of symmetric-key cryptography: Secret key must be sent before using, so secret key itself is vulnerable to being intercepted.
We resolve the vulnerable key with another approach called….
PUBLIC-KEY CRYPTOGRAPHY
In PUBLIC-KEY CRYPTOGRAPHY, also known as asymmetric key cryptography the idea is that you have two different keys: public key and private key. The two keys are cryptographically related, but impossible to decipher private key from public key.
The receiver makes its public key available to senders, and keeps the the private key itself. The receiver can share its public key as much as it likes, but holds onto its private key for itself and does not share with anybody. Then if a sender wants to send a message to a receiver, then just as with symmetric key cryptography. The sender will use encryption algorithm, along with a key to encrypt the message, but in this case, the sender will use the public key of the receiver for this process. The public key works in such a way that it can only be used to encrypt the message. It cannot decrypt it and it encrypts it in such a way that the only corresponding private key, which only the receiver knows can decrypt it. So if a third party was able to intercept the message, even if they had a public key, they still could not decrypt it, without going through a lot of trouble.
PUBLIC-KEY & SYMMETRIC-KEY
Now, why would anyone use symmetric key cryptography at all when you could use public key cryptography?
Public-key cryptography is an expensive process, must be used sparingly because it would drain resources to use it all the time. Instead what public key cryptography is used for primarily is to help two sides of a communication channel, agree on a common secret key that they can both use for symmetric key cryptography. Then once the same secret key can be agreed upon they use that afterward, so the two cryptographic methods work together.
SECURE SOCKET LAYER (SSL) TRANSPORT LAYER SECURITY (TLS)
Two main cryptographic protocols (SSL) and (TLS) are used for communication on computer networks are called the secure socket layer and transport layer security.
SSL was first, and TLS evolved from SSL and has now replaced it as official cryptographic protocol for the web. They are very similar, but SSL was officially deprecated by the IETF in 2015.
So now, TLS is now the official cryptographic protocol for the web. However, it is still common to hear the protocol referred to as SSL or SSL TLS.
SSL TLS ensures a secure channel between sender and receiver over an inherently insecure network, like the internet through the use of a public key and symmetric key cryptography in the way that that we’ve discussed.
SECURE CHANNEL
When we talk about a secure channel, let us keep in mind that there are two separate priorities.
A secure channel has to keep in mind:
- Privacy of the message so that third parties cannot read it
- Integrity of the message so that third parties cannot alter it
How does TLS achieve these goals? Through what is called a handshaking process.
TLS HANDSHAKING
The handshaking process is a complex one, and it can vary in implementation.
Basic overview
These steps below ensure both Privacy and Integrity.
- Client tells server it want to begin secure communication.
- Server send back SSL certificate with its certified public key
- Client (browser) uses certificate to verify server’s identity
- Client then generates random string called pre-master secret, encrypts it with server’s public key, sends it to server
- Server and client now have same pre-master secret, both generate identical session keys from it
- Session key then used for symmetric-key cryptography
HTTPS is HTTP with TLS added for privacy and integrity.
PUBLIC & PRIVATE KEYS
You have learned that a server needs a public key and a private key for public key cryptography. How does a server obtain this? If you are running a real production server and allowing users to access your server. Then you need to go through a certification process to obtain a SSL certificate. You need to go through what is known as a certificate authority (CA), which usually requires a fee. These are trusted third parties, which are often corporations such as: Comodo, Symantec and others. You would go to one of these authorities and follow their process to verify your credentials with them. If you satisfy their process, then they will issue you a SSL certificate with a public key that you can distribute and as well as a corresponding private key for you to use on your server side. Then all the mainstream browsers have built in ability to verify if certificates are issued by established CA’s.
But for our testing, we can use free OpenSSL tool to generate a self-aligned certificate for use in learning & development.
OPENSSL
Self-signed certificates are not acceptable in production because browsers will show warning to users that connection is not safe.
A warning looks like this:
But for testing purpose on your own computer with your own code using OPENSSL works fine.
OpenSSL is free and is usually already installed on MacOS.
NODE MODULES
Node provides a core HTTPS module as well as HTTP. Well use the core to set up HTTPS server in our Express app.
We will also use the core fs (filesystem) module’s .readFileSync() method to access private key and SSL certificate files, because the private key and the certificate that we generate will be stored on our server side as files, so we will need the fs module to access those files.
So you now have a basic overview of HTTP and how it leverages the use of both public key cryptography and symmetric key cryptography for ensuring secure communication between the client and the server.
Exercise: HTTPS and Secure Communication
- Explore the use of the HTTPS core Node module to create and run a secure server.
- Generate a private key and public certificate and use them to configure the HTTPS server.
- Redirect traffic from the insecure HTTP server to a secure HTTPS server.
Generate a private key and self-signed certificate using OpenSSL
- OpenSSL is typically installed by default on both MacOS and Windows. Enter openssl version into your bash terminal to confirm that you have it installed.
- If you for some reason you do not, then for MacOS, you can install it using Homebrew with the command brew install openssl. For Windows, you can download an installer for Windows here – use the second installer on the list at that webpage (Win64 OpenSSL v1.1.1d, 43MB), the EXE version.
- In your bash terminal open to the nucampsiteServer/bin folder (the bin folder inside the nucampsiteServer), create the private key and certificate by typing the following at the prompt: openssl req -nodes -new -x509 -keyout server.key -out server.cert
- You can press enter through the first 5 prompts. When asked to enter the Common Name, enter localhost. Enter your email address when asked.
- You should now see two new files in your nucampsiteServer/bin folder: server.key and server.cert.
- If you accidentally created these files outside of the bin folder, move them to that folder.
- If using Git, as a best practice, add your key and cert (bin/server.key and bin/server.cert) to your .gitignore file so that if you push to an online repository, they are not available to others.
Configuring the HTTPS server
- Open the www file in the bin directory and update its contents as follows:
. . .
const https = require('https');
const fs = require('fs');
. . .
app.set('secPort', port+443);
. . .
/**
* Create HTTPS server.
*/
const options = {
key: fs.readFileSync(__dirname+'/server.key'),
cert: fs.readFileSync(__dirname+'/server.cert')
};
const secureServer = https.createServer(options, app);
/**
* Listen on provided port, on all network interfaces.
*/
secureServer.listen(app.get('secPort'), () => {
console.log('Server listening on port', app.get('secPort'));
});
secureServer.on('error', onError);
secureServer.on('listening', onListening);
. . .
- Open app.js and add the following code to the file:
. . .
// Secure traffic only
app.all('*', (req, res, next) => {
if (req.secure) {
return next();
} else {
console.log(`Redirecting to: https://${req.hostname}:${app.get('secPort')}${req.url}`);
res.redirect(301, `https://${req.hostname}:${app.get('secPort')}${req.url}`);
}
});
. . .
- Make sure the mongodb server is running, then start the server (be sure you are in the nucampsiteServer directory and not the nucampsiteServer/bin directory).
- Start server with npm start
- If you are using the Chrome browser, go to this URL: chrome://flags/#allow-insecure-localhost and enable the yellow-highlighted setting “Allow invalid certificates for resources loaded from localhost.”
- Test in your browser by going first to https://localhost:3443. Now see the warning. We are getting this warning because we are not using a self-signed certificate and the browser does not recognize it as valid. It is OK to accept since this is our own server. But be careful on any public sites when you see this.
- Click the advance button and proceed.
- Next, go to http://localhost:3000 and observe what happens. Be sure to type http instead of https this time.
- Optional: Commit your changes to your Git repository with the message “HTTPS”.
- Warning: Before you commit, as a best practice, first add your key and cert (bin/server.key and bin/server.cert) to your .gitignore file so that if you push to an online repository, they are not available to others.
Uploading Files
File uploads are a common feature supported by many servers, often by client-side form field to choose & upload file to server side. Afterward, the server may need to do additional things with that file. Suppose you are filling out a form with information about a campsite and you are able to upload an image of a campsite, through that form. In this case, the server needs to be set up to accept that file upload and save it to a folder, where it can be accessed through the internet. Then it also has to save the URL for that image file in that campsite’s document in the database, so it can be accessed and displayed by a client in the future.
Server must be set up to accept upload and deal with file afterward:
- Client uploads campsite image, server must accept it
- Then save it to folder where it can be publicly accessed
- Then save URL for image to campsite document in database
CLIENT-SIDE UPLOAD THROUGH FORM
File uploads are typically supported through a form input with HTML, you would use the input element and specify the type as file and a name as well. In this HTML snippet below, you can see that the input type is set to file and a name is set for the input field.
This results in the HTML form that you would see on the client side.
Then when this form is submitted it will use the URL in the form action field, which in the example above is set to the slash image upload path. So on the server side, you would need to set up an endpoint to handle the file upload, then the client needs to set two more form attributes for uploading a file. The method to POST and the enctype (encoding type) to “multipart/form-data”.
With this type enctype: mutlipart/form-data the outgoing request message will be divided into multiple parts, with the help of a boundary value. Here is the log of a request header from a file upload, you can see that it automatically defines a boundary value, where it says boundary= then a bunch of dashes followed by a random series of numbers. That is the boundary value that would be used to divide up the file, then the server will know how to put the file back together by looking for this boundary value.
But how to we actually extract the file from the request body and put it back together and save it to the server? We will be using a middleware library called MULTER to help us with that.
Multer
Multer is a middleware library written on top of another library called Buseboy.
Busboy is a middleware for processing incoming HTML form data, and Multer add ability to process mutlipart/form-data.
When multer is enabled in your application, it will add two properties (file/files and body) to the request object as objects. Depending if you are uploading a single file or multiple files.
- body: contains text from any form text fields
- file/files: contains either a file + additional info about file, or an array containing multiple files + additional info per file.
- Learn to use the Multer middleware library to set up an Express server to accept file uploads
Install Multer
- At the prompt in your nucampsiteServer project, type the following to install Multer: npm install multer@1.4.2
Enable file uploading
- Add a new Express router named uploadRouter.js in the routes folder and add the following code to it:
const express = require('express');
const authenticate = require('../authenticate');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/images');
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
const imageFileFilter = (req, file, cb) => {
if(!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('You can upload only image files!'), false);
}
cb(null, true);
};
const upload = multer({ storage: storage, fileFilter: imageFileFilter});
const uploadRouter = express.Router();
uploadRouter.route('/')
.get(authenticate.verifyUser, authenticate.verifyAdmin, (req, res) => {
res.statusCode = 403;
res.end('GET operation not supported on /imageUpload');
})
.post(authenticate.verifyUser, authenticate.verifyAdmin, upload.single('imageFile'), (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(req.file);
})
.put(authenticate.verifyUser, authenticate.verifyAdmin, (req, res) => {
res.statusCode = 403;
res.end('PUT operation not supported on /imageUpload');
})
.delete(authenticate.verifyUser, authenticate.verifyAdmin, (req, res) => {
res.statusCode = 403;
res.end('DELETE operation not supported on /imageUpload');
});
module.exports = uploadRouter;
- Then update app.js to import the uploadRouter and configure the new route as follows:
. . .
const uploadRouter = require('./routes/uploadRouter');
. . .
app.use('/imageUpload', uploadRouter);
. . .
- Save all the changes
- Make sure MongoDB is running
- npm start
- Test your server using Postman.
- Optional: Make a Git commit with the message “File Upload”.
Difference between PUT and POST
Additional Services:
- How HTTPS Works (in illustrated comic form!)
- MDN – Web Security
- HackerNoon – Public Key Cryptography Simply Explained
- HTTPS
- Public Key Cryptography
- Transport Layer Security
- Kurose, James F., and Keith W. Ross. Computer networking: a top-down approach. Pearson, 2017, ISBN-10: 0134522206 • ISBN-13: 9780134522203.
- Let’s Encrypt: Non-Profit Free OpenSSL Certificate Authority
- Howto: Make Your Own Cert With OpenSSL on Windows
- OpenSSL for Windows
- How to Use SSL/TLS with Node.js
- Adding HTTPS (SSL) to Express 4.X Applications
- Node.js – https core module
- Node.js – fs.readFileSync()
- Express – app.set(), app.get()
- Express – app.all()
- Express – req.secure
- OpenSSL
- FlavioCopes – An Express HTTP Server with a Self-Signed Certificate
- W3Schools – HTML <input type=”file”>
- W3Schools – HTML <form> method Attribute
- Dev.to article: Understanding HTML Form Encoding
- NPM – multer
- NPM – Multer
- Alligator.io – Uploading Images to a Node.js Backend Using Multer and Express
- TutsPlus – File Upload with Multer in Node.js and Express