Skip to main content

Insecure object reference

Need

Enforce secure object references to prevent unauthorized access to user data

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();

let users = [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' },
{ id: 3, name: 'User 3' },
];

app.get('/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find((u) => u.id === userId);

if (user) {
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});

app.put('/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find((u) => u.id === userId);

if (user) {
user.name = req.body.name;
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});

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

The vulnerability in this code is an insecure object reference. It occurs because the code allows users to access and modify data of other users by simply modifying the value of the id parameter in the URL.

In the app.get('/users/:id') route, the code retrieves the id parameter from the URL and converts it to an integer using parseInt(). It then searches for a user in the users array based on the provided id. If a user is found, the code returns the user's information. However, if the user is not found, it returns a 404 error.

Similarly, in the app.put('/users/:id') route, the code retrieves the id parameter from the URL and converts it to an integer. It then searches for a user in the users array based on the provided id. If a user is found, the code updates the user's name with the value from the request body and returns the updated user's information. If the user is not found, it returns a 404 error.

The vulnerability lies in the fact that there is no check to ensure that the user making the request is authorized to access or modify the requested user's data. Any user can modify the id parameter in the URL to access and modify the data of other users.

To fix this vulnerability, the code should implement proper authorization and validation checks. It should ensure that unprivileged users can only access and modify their own information. Additionally, the code should handle user operations using session objects to maintain user-specific context and prevent unauthorized access to other users' data.

Steps

  • Implement authentication and authorization mechanisms to ensure that only authorized users can access and modify user data.
  • Use session objects or tokens to manage user sessions and validate user operations.
  • When retrieving user data, validate that the user making the request has the necessary permissions to access that data.
  • When updating user data, validate that the user making the request has the necessary permissions to modify that data.
  • Consider implementing role-based access control to define different levels of access for different user roles.
  • Encrypt sensitive user data to protect it from unauthorized access.
  • Regularly review and update the authorization mechanisms to address any new vulnerabilities or security risks.

Compliant code

import express from 'express';

const app = express();

let users = [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' },
{ id: 3, name: 'User 3' },
];

// Middleware for authentication and authorization
const authenticateUser = (req, res, next) => {
// Implement your authentication logic here
// Check if the user is authenticated and has the necessary permissions
// You can use session objects or tokens to manage user sessions
// If the user is not authenticated or authorized, return an error response
// Otherwise, continue to the next middleware or route handler
next();
};

app.get('/users/:id', authenticateUser, (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find((u) => u.id === userId);

if (user) {
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});

app.put('/users/:id', authenticateUser, (req, res) => {
const userId = parseInt(req.params.id);
const user = users.find((u) => u.id === userId);

if (user) {
// Check if the user has the necessary permissions to modify the data
// For example, you can compare the user ID with the ID in the session or token
// If the user is not authorized, return an error response
// Otherwise, update the user data
user.name = req.body.name;
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});

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

The fixed code addresses the vulnerability by implementing authentication and authorization mechanisms to ensure that users can only access and modify their own information.

  1. The code defines a middleware function called authenticateUser that is responsible for authenticating and authorizing the user. This function is executed before the route handlers for /users/:id and /users/:id endpoints.

  2. Inside the authenticateUser middleware, you can implement your own authentication logic. This can involve checking if the user is authenticated and has the necessary permissions to access or modify the data. You can use session objects or tokens to manage user sessions.

  3. In the route handlers for /users/:id and /users/:id endpoints, the authenticateUser middleware is added as a parameter. This ensures that the middleware is executed before the route handlers, allowing for authentication and authorization checks to be performed.

  4. When handling a GET request to /users/:id, the code retrieves the id parameter from the request URL and searches for a user with the matching ID in the users array. If a user is found, it is returned as a JSON response. If no user is found, a 404 error response is returned.

  5. When handling a PUT request to /users/:id, the code follows a similar process. It retrieves the id parameter from the request URL and searches for a user with the matching ID in the users array. If a user is found, the code checks if the user has the necessary permissions to modify the data. If authorized, the user's name is updated with the value from the request body, and the updated user is returned as a JSON response. If the user is not found or not authorized, a 404 error response is returned.

By implementing authentication and authorization checks using session objects or tokens, the fixed code ensures that unprivileged users can only access and modify their own information, mitigating the insecure object reference vulnerability.

References