Skip to main content

Password change without identity check

Need

Enforce strong identity verification for password changes

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of Shelf for managing and organizing data in a Node.js application

Description

Non compliant code

import 'package:shelf/shelf.dart' as shelf;

class User {
String username;
String password;

User({required this.username, required this.password});
}

List<User> users = [
User(username: 'test', password: 'test'),
];

shelf.Response handleRequest(shelf.Request request) {
var updatedPassword = request.url.queryParameters['newPassword'];
var username = request.url.queryParameters['username'];

var user = users.firstWhere((user) => user.username == username);
user.password = updatedPassword;

return shelf.Response.ok('Password Updated Successfully');
}

The above code is a simple representation of a Dart backend using the Shelf framework. A list of users is created for demonstration purposes. The handleRequest function is vulnerable to a password change without identity check.

In the handleRequest function, the updatedPassword and username are extracted from the request parameters. The function then finds the user with the provided username and updates the password directly without checking the old password or any other form of identity verification.

This means that anyone who knows a username can change the password of the corresponding user without needing to provide the old password or any other form of identity verification. This is a security vulnerability as it can lead to unauthorized access to user accounts.

Steps

  • Add an authentication middleware to the server that checks if the user is authenticated before processing the request.
  • In the handleRequest function, before changing the password, verify the identity of the user. This could be done by asking for the old password or by using a two-factor authentication method.
  • If the identity verification is successful, then proceed with the password change. If not, return an error response.

Compliant code

import 'package:shelf/shelf.dart' as shelf;

class User {
String username;
String password;

User({required this.username, required this.password});
}

List<User> users = [
User(username: 'test', password: 'test'),
];

shelf.Middleware checkAuthentication() {
return (shelf.Handler innerHandler) {
return (shelf.Request request) async {
var username = request.url.queryParameters['username'];
var oldPassword = request.url.queryParameters['oldPassword'];

var user = users.firstWhere((user) => user.username == username);

if (user.password != oldPassword) {
return shelf.Response.forbidden('Invalid old password');
}

return innerHandler(request);
};
};
}

shelf.Response handleRequest(shelf.Request request) {
var updatedPassword = request.url.queryParameters['newPassword'];
var username = request.url.queryParameters['username'];

var user = users.firstWhere((user) => user.username == username);
user.password = updatedPassword;

return shelf.Response.ok('Password Updated Successfully');
}

void main() {
var handler = const shelf.Pipeline()
.addMiddleware(checkAuthentication())
.addHandler(handleRequest);

shelf.serve(handler, 'localhost', 8080);
}

The code above fixes the vulnerability by adding an authentication middleware to the server that checks if the user is authenticated before processing the request. This is done in the checkAuthentication function, which is added to the server pipeline before the handleRequest function.

In the checkAuthentication function, the username and old password are extracted from the request parameters. The function then checks if there is a user with the given username and if the old password matches the user's current password. If the old password does not match, the function returns a forbidden response, preventing the request from being processed further.

In the handleRequest function, the new password is extracted from the request parameters and the user's password is updated. This function is only called if the checkAuthentication function does not return a forbidden response, ensuring that the user's password is only updated if the old password is correct.

Finally, in the main function, the checkAuthentication middleware is added to the server pipeline before the handleRequest function using the addMiddleware method. This ensures that the authentication check is performed before the request is processed.

References