Skip to main content

Business information leak - Token

Need

Protection of sensitive user information in session tokens

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 {
final String username;
final String email;
final String fullName;

User(this.username, this.email, this.fullName);

Map<String, String> toJson() => {
'username': username,
'email': email,
'fullName': fullName,
};
}

shelf.Response handleRequest(shelf.Request request) {
User user = User('testUser', '[email protected]', 'Test User');

return shelf.Response.ok('Successful login', headers: {
'Set-Cookie': 'session=${user.toJson()}',
});
}

The above code is an example of a Dart backend application using the Shelf framework. The handleRequest function is supposed to handle a user login request.

After a successful login, it creates a new User object with the username, email, and full name of the user. Then, it converts this User object to a JSON object and includes it in the session cookie.

This is a vulnerability because it exposes sensitive user information (username, email, and full name) in the session token. If an attacker can get access to this token, they can retrieve this sensitive information. This is a violation of the principle of least privilege, which states that a user should only have access to the information and resources necessary for its legitimate purpose.

Steps

  • Create a unique session ID for each user session instead of using user information.
  • Store the session ID on the server side, along with the user information.
  • When a user logs in, generate a new session ID and store it in the 'Set-Cookie' header.
  • On the server side, associate this session ID with the user's information.
  • When a request comes in with a session ID, look up the associated user information on the server side.
  • This way, no user information is sent to the client side, preventing information leak.

Compliant code

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

class User {
final String username;
final String email;
final String fullName;

User(this.username, this.email, this.fullName);

Map<String, String> toJson() => {
'username': username,
'email': email,
'fullName': fullName,
};
}

Map<String, User> sessions = {};

String generateSessionId() {
var random = Random();
var values = List<int>.generate(16, (i) => random.nextInt(256));
return values.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
}

shelf.Response handleRequest(shelf.Request request) {
User user = User('testUser', '[email protected]', 'Test User');
String sessionId = generateSessionId();

sessions[sessionId] = user;

return shelf.Response.ok('Successful login', headers: {
'Set-Cookie': 'session=$sessionId',
});
}

The updated code now generates a unique session ID for each user session instead of using user information. This session ID is stored on the server side, along with the user information. When a user logs in, a new session ID is generated and stored in the 'Set-Cookie' header. On the server side, this session ID is associated with the user's information. When a request comes in with a session ID, the associated user information is looked up on the server side. This way, no user information is sent to the client side, preventing information leak.

The generateSessionId function generates a random 32-character hexadecimal string that is used as the session ID. The sessions map stores the association between session IDs and user information. In the handleRequest function, a new session ID is generated for each login request, and this session ID is associated with the user's information in the sessions map. The session ID is then sent to the client side in the 'Set-Cookie' header.

References