Skip to main content

Lack of protection against brute force attacks - Credentials

Need

Enhanced protection against brute force attacks on promotional code credentials

Context

  • Usage of TypeScript for statically typed JavaScript development
  • Usage of Express for building web applications and APIs

Description

Non compliant code

import express from 'express';

const app = express();

app.post('/login', (req, res) => {
const { username, password } = req.body;

// Vulnerable code: No protection against brute force attacks
if (username === 'admin' && password === '123456') {
res.status(200).json({ message: 'Login successful' });
} else {
res.status(401).json({ message: 'Invalid credentials' });
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The vulnerability in this code is the lack of protection against brute force attacks on the login functionality. Brute force attacks involve systematically guessing all possible combinations of usernames and passwords until a valid one is found.

In the code, the /login endpoint accepts a POST request with a username and password in the request body. It then compares the provided credentials with a hardcoded username and password (admin and 123456 respectively). If the credentials match, a successful login response is sent. Otherwise, an invalid credentials response is sent.

The problem with this code is that there are no measures in place to prevent or mitigate brute force attacks. An attacker can repeatedly send requests with different username and password combinations until they find the correct ones. This increases the chances of successfully guessing valid credentials and gaining unauthorized access to the system.

To address this vulnerability, it is recommended to implement controls to prevent automated attacks and ensure that the login function is not executed by a robot. This can be done by implementing measures such as CAPTCHA, blocking by delay in the number of failed attempts, or other similar mechanisms. These controls can help protect against brute force attacks by adding an extra layer of security and making it more difficult for attackers to guess valid credentials.

Steps

  • Implement a rate limiting mechanism to limit the number of login attempts per IP address or user account.
  • Add a delay between login attempts to slow down automated attacks.
  • Implement a strong password policy to prevent easy guessing of passwords.
  • Consider using a CAPTCHA or other challenge-response mechanism to verify that the login attempt is made by a human.
  • Consider implementing multi-factor authentication to add an extra layer of security.
  • Regularly monitor and analyze login attempts to detect and block suspicious activity.
  • Keep the application and its dependencies up to date to ensure any security patches are applied.

Compliant code

import express from 'express';
import rateLimit from 'express-rate-limit';
import slowDown from 'express-slow-down';
import bcrypt from 'bcrypt';

const app = express();

// Rate limiting configuration
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // Limit each IP to 5 requests per windowMs
});

// Slow down configuration
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000, // 15 minutes
delayAfter: 3, // Delay after 3 requests
delayMs: 1000, // Delay each request by 1 second
});

app.use(express.json());
app.use(limiter);
app.use(speedLimiter);

app.post('/login', (req, res) => {
const { username, password } = req.body;

// Implement strong password policy and hash the password
const hashedPassword = bcrypt.hashSync(password, 10);

// Check if the username and hashed password match
if (username === 'admin' && bcrypt.compareSync(password, hashedPassword)) {
res.status(200).json({ message: 'Login successful' });
} else {
res.status(401).json({ message: 'Invalid credentials' });
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The fixed code addresses the lack of protection against brute force attacks by implementing rate limiting and slowing down mechanisms.

First, the code imports the necessary modules: express for creating the server, rateLimit for rate limiting requests, slowDown for slowing down requests, and bcrypt for hashing passwords.

The code then creates an instance of the Express application and configures the rate limiting and slowing down middleware. The rateLimit middleware limits the number of requests per IP address within a specified time window (15 minutes in this case) to 5 requests. This helps prevent automated attacks by restricting the number of attempts an attacker can make.

The slowDown middleware introduces a delay after a certain number of requests (3 requests in this case) and delays each subsequent request by 1 second. This further slows down automated attacks, making it harder for attackers to guess valid credentials quickly.

The code sets up a route for the /login endpoint using the app.post method. In this route, it retrieves the username and password from the request body.

To enhance security, the code uses the bcrypt module to hash the password before comparing it with the stored hashed password. The bcrypt.hashSync function hashes the password with a salt and a cost factor of 10. This ensures that the password is securely hashed and makes it computationally expensive for an attacker to guess the original password.

The code then checks if the provided username and the hashed password match. If they do, it sends a successful login response with a status code of 200. If the credentials are invalid, it sends an unauthorized response with a status code of 401.

Finally, the code starts the server on port 3000 and logs a message to indicate that the server is running.

Overall, the fixed code implements rate limiting, slowing down, and password hashing to protect against brute force attacks and increase the security of the login functionality.

References