Defending from Vulnerabilities: Bypass in Password Change Functionality

Password changes and updates after authentication are common functionalities found in most applications. While each application implements them differently, it is essential to ensure they are securely implemented.

Defending from Vulnerabilities: Bypass in Password Change Functionality

Password changes and updates after authentication are common functionalities found in most applications. While each application implements them differently, it is essential to ensure they are securely implemented.

In this week's series, we will explore a specific vulnerability in password change functionality. Let's assume that your application has implemented a password verification mechanism where the old password is validated in one request, and the new password is validated and set in a subsequent request. However, this separation allows an attacker to bypass the old password check and directly perform the second request to change the password. Additionally, the second request does not validate authentication, leading to an unauthenticated account takeover. This scenario highlights the critical need to secure sequential operations and ensure the authentication of sensitive actions.

Understanding the Attack

  1. Password Change Flow:
    • The user fills the password change form with old and new password.
    • The application submits a request to validate the user's old password.
    • Upon successful validation, the application initiates and submits a second request to set the new password. The request also contains the user ID as the user's identifier.
  2. Attack Vector:
    • The attacker directly sends the second request to set a new password.
    • The second request lacks authentication validation, allowing the attacker to change the password without verifying the old password or being authenticated.

Defensive Strategy

While this attack is not common, it is possible if your application validates in a multi-step process. To defend against this specific password change attack scenario, follow these steps:

  1. Combine Validation and Change in One Request: To prevent bypassing the old password validation, validate the old password and set the new password within a single request.

JS Example:

const express = require('express');
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const { User } = require('./models'); // Assuming User is your user model

const app = express();
app.use(bodyParser.json());

app.post('/change-password', async (req, res) => {
  const { userId, oldPassword, newPassword } = req.body;

  // Fetch user from the database
  const user = await User.findByPk(userId);
  if (!user) {
    return res.status(404).send('User not found');
  }

  // Validate old password
  const isMatch = await bcrypt.compare(oldPassword, user.password);
  if (!isMatch) {
    return res.status(403).send('Incorrect old password');
  }

  // Hash and set the new password
  const hashedPassword = await bcrypt.hash(newPassword, 10);
  user.password = hashedPassword;
  await user.save();

  res.send('Password changed successfully');
});

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

2. Ensure Authentication on Sensitive Requests: Validate the user’s authentication status on every request that performs sensitive actions, such as changing a password.

Use middleware to ensure that only authenticated users can access the password change endpoint.

JS Example:

const express = require('express');
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const { User } = require('./models'); // Assuming User is your user model
const { isAuthenticated } = require('./authMiddleware'); // Middleware for authentication

const app = express();
app.use(bodyParser.json());

app.post('/change-password', isAuthenticated, async (req, res) => {
  const { oldPassword, newPassword } = req.body;
  const userId = req.user.id; // Assuming user ID is stored in req.user by the middleware

  // Fetch user from the database
  const user = await User.findByPk(userId);
  if (!user) {
    return res.status(404).send('User not found');
  }

  // Validate old password
  const isMatch = await bcrypt.compare(oldPassword, user.password);
  if (!isMatch) {
    return res.status(403).send('Incorrect old password');
  }

  // Hash and set the new password
  const hashedPassword = await bcrypt.hash(newPassword, 10);
  user.password = hashedPassword;
  await user.save();

  res.send('Password changed successfully');
});

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

Authentication Middleware Example:

const isAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).send('Unauthorized');
};

module.exports = { isAuthenticated };
  1. Use CSRF Protection: Implement CSRF protection on forms to prevent unauthorized actions from malicious websites.

JS Example:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.use(csrfProtection);

app.post('/change-password', isAuthenticated, csrfProtection, async (req, res) => {
  const { oldPassword, newPassword } = req.body;
  const userId = req.user.id;

  // Fetch user from the database
  const user = await User.findByPk(userId);
  if (!user) {
    return res.status(404).send('User not found');
  }

  // Validate old password
  const isMatch = await bcrypt.compare(oldPassword, user.password);
  if (!isMatch) {
    return res.status(403).send('Incorrect old password');
  }

  // Hash and set the new password
  const hashedPassword = await bcrypt.hash(newPassword, 10);
  user.password = hashedPassword;
  await user.save();

  res.send('Password changed successfully');
});

Conclusion

By combining the old password validation and new password setting into a single request, ensuring authentication on all sensitive requests, and implementing CSRF protection, you can effectively mitigate the risk of password change bypass attacks in your application.

This detailed approach helps developers and security engineers understand and apply these protections in their specific scenarios.

Stay tuned for more specific attack scenarios and defensive strategies in our weekly series.