Shoulder.dev Logo Shoulder.dev

Implementing Security Measures in benhall/express-demo

Scenario: You have built a web application using the benhall/express-demo project and now want to secure it against common threats. In this guide, we will cover implementing authentication and authorization, encrypting sensitive data, and validating user input.

Solution:

  1. Authentication and Authorization:

First, let’s secure our application by implementing authentication and authorization using middleware. We will use Passport.js, an Express-compatible authentication middleware for Node.js.

a. Install Passport.js:

Add the following line to your package.json file under dependencies:

"passport": "^0.4.2"

b. Create a new file app.js in the routes directory and set up Passport.js:

const express = require('express');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');

// User model
const User = require('../models/user');

// Configure Passport.js
passport.use(new LocalStrategy({ usernameField: 'email' },
async function(email, password, done) {
const user = await User.findOne({ email: email });
if (!user) return done(null, false, { message: 'Incorrect email.' });

const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) return done(null, false, { message: 'Incorrect password.' });

return done(null, user);
}
));

passport.serializeUser(function(user, done) {
done(null, user.id);
});

passport.deserializeUser(async function(id, done) {
const user = await User.findById(id);
done(null, user);
});

// Create a new Express router
const router = express.Router();
router.use(passport.initialize());
router.use(passport.session());

// Protect routes
router.get('/protected', isAuthenticated, function(req, res) {
res.send('Welcome to the protected section!');
});

// Define middleware
function isAuthenticated(req, res, next) {
if (req.isAuthenticated()) return next();

res.redirect('/login');
}

module.exports = router;
  1. Encrypting Sensitive Data:

To encrypt sensitive data, we will use the bcryptjs library. Update your package.json file under dependencies to include it:

"bcryptjs": "^2.4.3"

Modify the User model in models/user.js to hash the password before saving it to the database:

const bcrypt = require('bcryptjs');

// ...

const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
validate: {
isEmail: true,
},
},
password: {
type: String,
required: true,
minlength: 6,
},
}, {
timestamps: true,
});

UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});

module.exports = mongoose.model('User', UserSchema);
  1. Validating User Input:

To validate user input, we will use Helmet.js, a set of middleware functions to help secure Express.js web applications by setting various HTTP headers. Install Helmet.js by adding it to your package.json file under dependencies:

"helmet": "^4.1.2"

Update your app.js file to include Helmet.js:

const helmet = require('helmet');

// ...

app.use(helmet());

Create a new file app.js in the routes directory and set up Helmet.js:

const helmet = require('helmet');

// Configure Helmet.js
app.use(helmet());

// Define middleware
function xssFilter() {
return function(req, res, next) {
res.set('X-XSS-Protection', '1; mode=block');
next();
};
}

// Apply middleware
app.use(xssFilter);

Tests:

To verify the security measures implemented, you can perform the following tests:

  1. Authentication and Authorization:
  • Attempt to access a protected route without being authenticated.
  • Attempt to access a protected route with incorrect credentials.
  • Attempt to access a protected route with valid credentials.
  1. Encrypting Sensitive Data:
  • Attempt to retrieve a user’s password from the database.
  • Attempt to modify a user’s password and verify it is encrypted in the database.
  1. Validating User Input:
  • Attempt to access the application with malicious input in the URL or headers.
  • Attempt to submit malicious input through forms.

These tests should help ensure that the security measures have been implemented correctly and effectively.