Skip to main content

Email uniqueness not properly verified

Need

Enforce email uniqueness during registration process

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.use(express.json());

interface User {
id: number;
email: string;
}

const users: User[] = [];

app.post('/register', (req, res) => {
const { email } = req.body;

if (!email) {
return res.status(400).json({ error: 'Email is required' });
}

const userExists = users.some((user) => user.email === email);
if (userExists) {
return res.status(400).json({ error: 'Email already exists' });
}

users.push({ id: users.length + 1, email });

return res.status(200).json({ message: 'User registered successfully' });
});

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

The vulnerability in this code lies in the email uniqueness check during the registration process. The code allows the plus character (+) in the email, which can be abused by an attacker to create multiple accounts pointing to the same email.

In the code, when a user tries to register, the email is extracted from the request body. If the email is not provided, a 400 Bad Request response is returned, indicating that the email is required.

Next, the code checks if the email already exists in the users array by using the some method. If a user with the same email already exists, a 400 Bad Request response is returned, indicating that the email already exists.

However, the code does not properly validate the uniqueness of the email. It only checks if there is any user with the exact same email in the users array. This means that an attacker can abuse the plus character in the email to create multiple accounts with different email variations, but all pointing to the same email inbox.

For example, if the email "[email protected]" is already registered, an attacker can register with "[email protected]", "[email protected]", and so on, creating multiple accounts that all point to the same email inbox.

This vulnerability allows an attacker to bypass the email uniqueness check and create multiple accounts with the same email, which can lead to abuse of the system or potential security issues.

Steps

  • Modify the User interface to include a unique identifier for each user.
  • Create a Set to store the unique emails.
  • Modify the userExists check to use the Set to check for email uniqueness.
  • Update the registration logic to add the email to the Set before adding the user to the users array.
  • Update the response messages to reflect the changes made.

Compliant code

import express from 'express';

const app = express();

app.use(express.json());

interface User {
id: number;
email: string;
}

const users: User[] = [];
const uniqueEmails = new Set<string>();

app.post('/register', (req, res) => {
const { email } = req.body;

if (!email) {
return res.status(400).json({ error: 'Email is required' });
}

if (uniqueEmails.has(email)) {
return res.status(400).json({ error: 'Email already exists' });
}

uniqueEmails.add(email);

const newUser: User = {
id: users.length + 1,
email,
};

users.push(newUser);

return res.status(200).json({ message: 'User registered successfully' });
});

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

The fixed code addresses the vulnerability by properly verifying the uniqueness of email addresses during the registration process.

First, the code imports the necessary dependencies, including the Express framework.

The code defines an interface called User which represents the structure of a user object. It has two properties: id of type number and email of type string.

Next, the code initializes an empty array called users to store registered users and a new Set called uniqueEmails to keep track of unique email addresses.

The code sets up an Express application and configures it to parse JSON data.

The code defines a POST route handler for the '/register' endpoint. Inside the handler, it extracts the email property from the request body.

The code then checks if the email is empty. If it is, it returns a 400 status code with an error message indicating that the email is required.

Next, the code checks if the uniqueEmails Set already contains the provided email. If it does, it returns a 400 status code with an error message indicating that the email already exists.

If the email is unique, the code adds it to the uniqueEmails Set to ensure uniqueness.

The code then creates a new User object with an id value based on the length of the users array plus one, and assigns the provided email to the email property.

The new user object is pushed to the users array.

Finally, the code returns a 200 status code with a success message indicating that the user has been registered successfully.

The code also starts the Express server and listens on port 3000.

By using a Set to keep track of unique email addresses and checking for duplicates before registering a new user, the code ensures that each email can only be associated with one user account, effectively addressing the vulnerability.

References