Skip to main content

User enumeration

Need

Prevention of user enumeration

Context

  • Usage of TypeScript for type-checking and compiling JavaScript code
  • Usage of Express for building web applications and handling HTTP requests

Description

Non compliant code

import express from 'express';

const app = express();

app.get('/users/:username', (req, res) => {
const { username } = req.params;

if (isUserExists(username)) {
res.status(200).json({ message: 'User exists' });
} else {
res.status(404).json({ message: 'User does not exist' });
}
});

function isUserExists(username: string): boolean {
// Check if the user exists in the database or any other data source
// Return true if the user exists, false otherwise
}

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

The vulnerability in this code is user enumeration. User enumeration occurs when an attacker is able to determine whether a username exists in the system by observing different responses from the server. In this case, the code provides different responses for existent and non-existent users.

In the vulnerable code, there is an endpoint /users/:username that accepts a username as a parameter. When a request is made to this endpoint, the code checks if the user exists by calling the isUserExists function. If the user exists, a response with status code 200 and a JSON message "User exists" is returned. If the user does not exist, a response with status code 404 and a JSON message "User does not exist" is returned.

This allows an attacker to enumerate valid usernames by sending requests to the /users/:username endpoint and observing the different responses. By analyzing the responses, the attacker can determine which usernames exist in the system and which do not.

To mitigate this vulnerability, it is recommended to set the same server response for existent and non-existent users. This can be done by returning a generic error message or response for all users, regardless of their existence in the system.

Steps

  • Remove the differentiation in server response for existent and non-existent users
  • Set the same server response for both existent and non-existent users

Compliant code

import express from 'express';

const app = express();

app.get('/users/:username', (req, res) => {
const { username } = req.params;

res.status(200).json({ message: 'User exists' });
});

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

The fixed code is written in TypeScript using the Express framework. It addresses the user enumeration vulnerability by ensuring that the server responds with the same message for both existent and non-existent users.

The code starts by importing the necessary dependencies, including the Express module. It then creates an instance of the Express application.

The application defines a single route handler for the /users/:username endpoint using the app.get() method. This endpoint expects a username parameter in the URL. Inside the route handler function, the username parameter is extracted from the request's params object.

Instead of providing different responses based on the existence of the user, the code always responds with a JSON object containing a message indicating that the user exists. This ensures that the server response is consistent and does not leak information about the existence of a user.

The response is sent using the res.status(200).json() method, which sets the HTTP status code to 200 (OK) and sends the JSON response to the client.

Finally, the application listens on port 3000 using the app.listen() method and logs a message to the console indicating that the server is running.

By setting the same server response for both existent and non-existent users, the fixed code eliminates the possibility of an attacker enumerating valid usernames through error messages, response times, or other techniques.

References