• Labs icon Lab
  • Core Tech
Labs

Challenge: Building a Secure Node.js Application

This challenge lab presents you with a functioning Express application that needs to be secured. You should be familiar with typical Node.js security measures and concepts such as HTTPS and middleware as you will be implementing some of these features to mitigate common security risks.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 30m
Published
Clock icon Oct 29, 2024

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Welcome to this Node.js Security Challenge Lab

    You have been provided an Express application that has API endpoints for displaying, creating, modifying, and removing products in a "database". However, it is lacking in some basic security features. It will be your responsibility to add some of these features throughout this lab.

    To get the application running and check your implementations, use the command npm run serverstart in the first terminal. In the second terminal, you will be using curl to send requests to your application server endpoints. For example, if you'd like to add a new item to the database, you'd use the command curl -X POST http://localhost:8080/products/create -H "Content-Type: application/json" -d '{"name": "Example Product MF781", "quantity": 22}'. Likewise, if you wanted to edit an item with an id of 3 in the database, you would do curl -X POST http://localhost:8080/products/3/update -H "Content-Type: application/json" -d '{"name": "New Name YL082", "quantity": 5}'. You can check routes/products.js and controllers/ProductController.js to see the endpoints and their behavior.

    If you'd like to reset your database, run the command npm run seedItems in either terminal. Make sure that the server is not running before you do so.

    Lastly, you are free to check your work with the solutions in the solutions folder. Again, the solutions don't need to be followed rigidly. If you are producing the same results or your implementations are functionally equivalent, then that's totally fine.

  2. Challenge

    Step 1: Input Validation

    The first step will be to incorporate input validation to sanitize user inputs. You will need the body module from express-validator in products.js. For each route in products.js, you will need to trim() the input and optionally provide a message if the validation fails using withMessage(). If the input is a 'name', you should escape() it and check this it is of an appropriate length with isLength(). The range can be chosen at your discretion. If the input is an 'id' or 'quantity', you should check that it is in an integer of at least 1 using isInt().

    For the /'create' endpoint, it accepts a 'name' and 'quantity'. The '/update' endpoint accepts an 'id', 'name', and 'quantity'. Lastly, the '/delete' endpoint accepts an 'id'.


    Route Handler

    Once you've inserted the validation middleware in the routes, you need to check the results of the validation and act accordingly within productController.js. You will need to import the validationResult module from express-validator. Within each route handler, call validationResult() on the request to obtain possible errors. If there are errors, return a status code 400 along with all the errors in json format.

  3. Challenge

    Step 2: Setting up HTTPS

    Next, you'll setup an HTTPS layer for the application and redirect any traffic from the HTTP server to it. In the www file, you will need to import the https and selfsigned modules. Normally HTTPS would utilize a certificate from a trusted certificate authority, but for the purpose of this lab the selfsigned module will provide a self-signed certificate to simulate a signed certificate. You can generate one with const pems = selfsigned.generate(null, { days: 365 });.

    Create an HTTPS server using the 'private' and 'cert' properties from your generated certificate. Set it to listen to the httpsPort, then hook it up with the 'error' and 'listening' events just like the HTTP server.

    Lastly, head over to app.js. You will need to setup redirection from the HTTP server to the HTTPS server using an app.use() statement. If the request is secure, it should redirect to 'https://localhost:8443' appended with the originalUrl of the request.


    Testing Your Results

    If implemented correctly, you can test it by running curl https://localhost:8443. You should get an error saying that this certificate chain is issued by an untrusted authority. Don't be alarmed, as this is to be expected due to your self-signed certificate. To bypass this, use curl -k https://localhost:8443. You should see a list of products in the database.

    You can test the redirection with curl http://localhost:8080. You should get a result saying Found followed by a redirection. You can execute the redirection with curl -L -k http://localhost:8080, which should also display a list of products in the database. You can pretty print the output by piping to json_pp. Moving forward, you will need to utilize curl with the aforementioned flags for any endpoints now that HTTPS has been setup.

  4. Challenge

    Step 3: Helmet and CORS

    Now, you will need to setup Helmet and CORS. Both of these features are implemented as middleware that sets your HTTP/S response headers to control what is allowed to access and interact with the application.

    To implement them, simply import helmet and cors into scope within app.js. Then make sure to inject them with app.use() before any other middleware, especially the HTTP redirection.

    For helmet, you should set the preload property of the strictTransportSecurity option to true, which will allow HSTS headers to be loaded from the HSTS registry. It won't work in this specific scenario, but it would be a good idea in a production environment.

    For CORS, you should create a corsOption variable with an origin property set to 'https://localhost:8443'. Use this object as the parameter when injecting cors.


    Check Your Implementations

    To test that the headers have been correctly setup, you can run either curl -I http://localhost:8080 or curl -I -k https://localhost:8443. You should see a list of headers including Content-Security-Policy and Strict-Transport-Security among many others for helmet. For CORS, you should see the Access-Control-Allow-Origin header.

  5. Challenge

    Lab Completion

    Congratulations on Finishing the Challenge

    You have successfully implemented some key security features into the Node.js/Express application.

    That being said, the type of configurations and settings you would need for a specific application in production will always be contextual, so you are encouraged to check out the docs for Helmet and CORS to further understand the settings you have at your disposal.

    Feel free to continue learning through the Node.js path, which includes a security course that covers many of the concepts in this lab and more.

George is a Pluralsight Author working on content for Hands-On Experiences. He is experienced in the Python, JavaScript, Java, and most recently Rust domains.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.