Insufficient data authenticity validation - Checksum verification - Dart

Insufficient data authenticity validation - Checksum verification - Dart

Need

Ensure proper data authenticity validation and checksum verification

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;

Future handleRequest(shelf.Request request) async {
  var client = http.Client();
  var response = await client.get('https://external-server.com/resource');

  // Load the data from the external server without any checksum verification
  var data = response.body;

  return shelf.Response.ok(data);
}
        
        

In the above code snippet, we are making a GET request to an external server to retrieve some data using the http.Client().get() method. The data retrieved from the external server is directly used in the application by assigning it to the data variable.

The vulnerability here is that the integrity of the data loaded from the external server is not validated. This means an attacker could potentially modify the data in transit, leading to the application processing incorrect, potentially harmful data. This is because there is no checksum verification in place to ensure the data has not been tampered with during transmission.

A checksum verification would involve generating a checksum of the original data on the server side and sending this checksum along with the data. On the client side (in this case, our application), the checksum of the received data would be calculated and compared with the received checksum. If they match, the data has not been tampered with. If they do not match, the data integrity is compromised and the data should not be trusted or used.

The lack of such a checksum verification mechanism in the code makes it vulnerable to attacks that compromise the integrity of the data.

Steps

  • First, you need to obtain a checksum for the resource you are downloading. This could be provided by the server or calculated after the resource is downloaded.
  • Next, you need to implement a function that can calculate the checksum of the downloaded resource. This function should use the same algorithm that was used to generate the original checksum.
  • After downloading the resource, you should calculate its checksum using the function you implemented.
  • Finally, you should compare the calculated checksum with the original one. If they match, the resource is valid and you can proceed. If they don't match, the resource may have been tampered with and you should not use it.

Compliant code

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

Future handleRequest(shelf.Request request) async {
  var client = http.Client();
  var response = await client.get('https://external-server.com/resource');

  // Load the data from the external server
  var data = response.body;

  // Obtain the original checksum from the server
  var originalChecksum = response.headers['checksum'];

  // Calculate the checksum of the downloaded resource
  var calculatedChecksum = sha256.convert(utf8.encode(data)).toString();

  // Compare the calculated checksum with the original one
  if (calculatedChecksum != originalChecksum) {
    throw Exception('Data integrity check failed. The resource may have been tampered with.');
  }

  return shelf.Response.ok(data);
}
        
        

The updated code now includes a checksum verification process to validate the integrity of the resources loaded from the external server.

First, we obtain the original checksum from the server. This is assumed to be provided in the 'checksum' header of the HTTP response.

Next, we calculate the checksum of the downloaded resource. We use the SHA-256 algorithm for this, which is a common choice for checksum calculations. The crypto package's sha256 function is used to calculate the checksum. The downloaded data is first encoded into a UTF-8 byte array, which is then passed to the sha256.convert function. The result is converted to a string to facilitate comparison with the original checksum.

Finally, we compare the calculated checksum with the original one. If they match, the resource is valid and we return it in the response. If they don't match, we throw an exception. This indicates that the resource may have been tampered with and should not be used.

References