Skip to main content

Insecure object reference - Financial information

Need

Secure access to financial information

Context

  • Usage of Dart 2.0 for building high-performance, cross-platform applications
  • Usage of Shelf for managing and organizing data in Node.js applications
  • Usage of shelf_io for managing and organizing data storage in Node.js applications
  • Usage of shelf_router for routing and handling HTTP requests in a Shelf application

Description

Non compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';

class CreditCard {
final String number;
final String accountID;
CreditCard(this.number, this.accountID);
}

class Account {
final String id;
final List<CreditCard> creditCards;
Account(this.id, this.creditCards);
}

final accounts = <Account>[
Account('1', [CreditCard('1234', '1')]),
Account('2', [CreditCard('5678', '2')]),
];

void main() async {
final app = Router();

app.get('/account/<accountId>/creditcard/<cardNumber>', (shelf.Request request, String accountId, String cardNumber) {
final account = accounts.firstWhere((a) => a.id == accountId);
final card = account.creditCards.firstWhere((c) => c.number == cardNumber);
return shelf.Response.ok('Card: ${card.number}, Account: ${card.accountID}');
});

final server = await io.serve(app, 'localhost', 8080);
print('Serving at http://${server.address.host}:${server.port}');
}

The above Dart code is a simple server application that uses the Shelf framework. It has a vulnerability related to insecure object reference.

The application has a list of Account objects, each with an associated list of CreditCard objects. The server has a single GET endpoint /account/<accountId>/creditcard/<cardNumber> which takes an account ID and a credit card number as parameters.

The vulnerability lies in the fact that the server does not check if the credit card number belongs to the account ID specified in the request. This means that a user can access the credit card information of another user by simply changing the accountId and cardNumber parameters in the URL.

For example, if a user with account ID '1' knows the credit card number '5678' of a user with account ID '2', they can access the credit card information of the latter by sending a GET request to /account/1/creditcard/5678. This could lead to unauthorized access to sensitive information, which is a serious security concern.

Steps

  • Implement an authorization check to verify if the account making the request is the same as the account associated with the credit card.
  • This check should be done before any data is returned in the '/account/<accountId>/creditcard/<cardNumber>' endpoint.
  • If the account making the request is not the same as the account associated with the credit card, return an error message indicating that the user does not have permission to access the data.
  • Consider using a middleware for authorization checks to avoid repeating the same code in different endpoints.

Compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';

class CreditCard {
final String number;
final String accountID;
CreditCard(this.number, this.accountID);
}

class Account {
final String id;
final List<CreditCard> creditCards;
Account(this.id, this.creditCards);
}

final accounts = <Account>[
Account('1', [CreditCard('1234', '1')]),
Account('2', [CreditCard('5678', '2')]),
];

void main() async {
final app = Router();

app.get('/account/<accountId>/creditcard/<cardNumber>', (shelf.Request request, String accountId, String cardNumber) {
final account = accounts.firstWhere((a) => a.id == accountId);
final card = account.creditCards.firstWhere((c) => c.number == cardNumber);

// Check if the account making the request is the same as the account associated with the credit card
if (card.accountID != accountId) {
return shelf.Response.forbidden('You do not have permission to access this data');
}

return shelf.Response.ok('Card: ${card.number}, Account: ${card.accountID}');
});

final server = await io.serve(app, 'localhost', 8080);
print('Serving at http://${server.address.host}:${server.port}');
}

The updated code includes an authorization check in the '/account/&lt;accountId>/creditcard/&lt;cardNumber>' endpoint. This check verifies if the account making the request is the same as the account associated with the credit card. If the account making the request is not the same as the account associated with the credit card, the server returns a 403 Forbidden response with an error message indicating that the user does not have permission to access the data. This prevents users from accessing credit card information associated with other accounts, thereby fixing the insecure object reference vulnerability.

References