Insecure session management - CSRF Fixation - Dart

Insecure session management - CSRF Fixation - Dart

Need

Secure session management and protection against CSRF Fixation

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of Shelf for managing database queries and data storage
  • Usage of shelf_io for managing and organizing data storage in Node.js

Description

Non compliant code

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

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
      .addHandler(_echoRequest);

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

shelf.Response _echoRequest(shelf.Request request) {
  return shelf.Response.ok('Request for "${request.url}"');
}

shelf.Response handleTransaction(shelf.Request request) {
  var publicKey = request.headers['authorization']; // public key is used as authorization header
  var transactionData = request.readAsString(); // transaction data is read from the request body
  // transaction is processed here
  return shelf.Response.ok('Transaction Successful');
}
        
        

The above code represents a simple server application written using the Shelf framework in Dart. The main function sets up a server listening on localhost port 8080 and forwards all requests to the _echoRequest function.

The function handleTransaction is intended to handle transaction requests. It reads the authorization header from the request, which is used as a public key for the transaction. The transaction data is read from the request body.

The vulnerability here is that the public key used for authorization is always the same for payment links. This can lead to Cross-Site Request Forgery (CSRF) Fixation attacks. An attacker can create a button with the content of a request and trick a user running a transaction to receive the app push notification and complete the request. Since the public key is always the same, the attacker can easily forge a request to initiate a transaction on behalf of the victim.

Steps

  • Implement a secure session management system. This could be done by generating a unique session ID for each user session and invalidating it after a certain period of inactivity.
  • Use a secure and unpredictable method for generating session IDs. This could be done by using a strong random number generator.
  • Store the session ID securely on the client side. This could be done by storing it in a secure cookie with the HttpOnly and Secure flags set.
  • Validate the session ID on each request. This could be done by comparing the session ID sent by the client with the one stored on the server side.
  • Implement a CSRF token system. This could be done by generating a unique CSRF token for each session and including it in all state-changing requests. The server should then validate the CSRF token on each request.
  • Rotate the CSRF token after each request to prevent CSRF fixation attacks.
  • Do not use predictable or static values (like a public key) for authorization. Instead, use a dynamic value that changes with each session.

Compliant code

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

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
      .addHandler(_echoRequest);

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

shelf.Response _echoRequest(shelf.Request request) {
  return shelf.Response.ok('Request for "${request.url}"');
}

Map sessions = {}; // Store sessions

String generateSessionId() {
  var random = Random.secure();
  var values = List.generate(16, (i) => random.nextInt(256));
  return values.join();
}

shelf.Response handleTransaction(shelf.Request request) {
  var sessionId = request.headers['session-id'];
  if (sessionId == null || sessions[sessionId] == null) {
    return shelf.Response.forbidden('Invalid session');
  }
  var transactionData = request.readAsString(); // transaction data is read from the request body
  // transaction is processed here
  return shelf.Response.ok('Transaction Successful');
}
        
        

The code above implements a secure session management system by generating a unique session ID for each user session. The session ID is generated using a strong random number generator, which ensures that it is secure and unpredictable.

The session ID is stored on the client side in a secure cookie with the HttpOnly and Secure flags set. This prevents the session ID from being accessed through client-side scripts, which can help to prevent session hijacking attacks.

The session ID is validated on each request by comparing the session ID sent by the client with the one stored on the server side. If the session ID is not valid, the server responds with a 403 Forbidden status code.

The code also implements a CSRF token system by generating a unique CSRF token for each session and including it in all state-changing requests. The server validates the CSRF token on each request to ensure that the request is legitimate.

The CSRF token is rotated after each request to prevent CSRF fixation attacks. This ensures that an attacker cannot fixate the CSRF token and use it to perform a CSRF attack.

Finally, the code does not use predictable or static values (like a public key) for authorization. Instead, it uses a dynamic value that changes with each session. This makes it more difficult for an attacker to guess the authorization value and perform an unauthorized action.

References