Skip to main content

Non-encrypted confidential information - DB

Need

Secure storage of confidential information in the database

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of dart:io for performing input/output operations in Dart
  • Usage of dart:convert for encoding and decoding data in Dart
  • Usage of the Shelf package in Dart for building web applications and HTTP servers
  • Usage of shelf_io.dart package for handling HTTP requests and responses in Dart Shelf framework

Description

Non compliant code

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

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

io.serve(handler, InternetAddress.loopbackIPv4, 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}

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

Future<String> readFile(String path) async {
final file = File(path);

if (!await file.exists()) {
return null;
}

return await file.readAsString();
}

Future<void> writeFile(String path, String data) async {
final file = File(path);

if (!await file.exists()) {
await file.create();
}

await file.writeAsString(data);
}

This Dart code uses the Shelf framework, a middleware system for Dart and is set up to serve at the localhost on port 8080. The main function sets up a handler pipeline that logs requests and handles them by echoing the request URL.

The readFile and writeFile functions are used to read and write data to a file. These functions are vulnerable because they do not encrypt the data they are handling.

The writeFile function takes a path and data as arguments. It checks if the file at the provided path exists, and if it does not, it creates the file. Then, it writes the provided data to the file as a string. This is a vulnerability because the data is written to the file without any form of encryption. If an attacker gains access to the file system, they can easily read the contents of the file and gain access to any sensitive information it contains.

The readFile function takes a path as an argument. It checks if the file at the provided path exists, and if it does, it reads the contents of the file as a string and returns it. This is a vulnerability because the data is read from the file without any form of decryption. If an attacker has modified the contents of the file, the application would read the modified contents without any way of knowing that they have been tampered with.

Steps

  • Encrypt the data before storing it in the database. You can use libraries like 'pointycastle' for Dart to encrypt the data.
  • Use secure methods to store sensitive data. Avoid storing sensitive data directly on the device. If necessary, use secure storage solutions like 'flutter_secure_storage'.
  • Implement access controls to restrict who can access the stored data. Only authorized users should be able to access sensitive data.
  • Regularly update and patch your system and libraries to protect against known vulnerabilities.
  • Implement proper error handling. Do not reveal sensitive information in error messages.
  • Use HTTPS for all communications between the client and server to protect data in transit.

Compliant code

import 'dart:io';
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:pointycastle/export.dart' as pc;

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

io.serve(handler, InternetAddress.loopbackIPv4, 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}

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

Future<String> readFile(String path) async {
final file = File(path);

if (!await file.exists()) {
return null;
}

String encryptedData = await file.readAsString();
String decryptedData = decryptData(encryptedData);
return decryptedData;
}

Future<void> writeFile(String path, String data) async {
final file = File(path);

if (!await file.exists()) {
await file.create();
}

String encryptedData = encryptData(data);
await file.writeAsString(encryptedData);
}

String encryptData(String data) {
final key = pc.KeyParameter(utf8.encode('mySecureKey'));
final iv = pc.IVParameter(utf8.encode('mySecureIV'));
final encrypter = pc.PaddedBlockCipher('AES/PKCS7')
..init(true, pc.ParametersWithIV(key, iv));
final encryptedData = encrypter.process(utf8.encode(data));
return base64.encode(encryptedData);
}

String decryptData(String data) {
final key = pc.KeyParameter(utf8.encode('mySecureKey'));
final iv = pc.IVParameter(utf8.encode('mySecureIV'));
final decrypter = pc.PaddedBlockCipher('AES/PKCS7')
..init(false, pc.ParametersWithIV(key, iv));
final decryptedData = decrypter.process(base64.decode(data));
return utf8.decode(decryptedData);
}

The updated code now includes encryption and decryption of data before writing to and reading from the file. The pointycastle library is used for this purpose.

The encryptData function takes a string of data, converts it to bytes, and then encrypts it using the AES algorithm with PKCS7 padding. The key and initialization vector (IV) are hardcoded for simplicity, but in a real-world application, they should be securely generated and stored. The encrypted data is then encoded to a base64 string for storage.

The decryptData function does the reverse: it takes a base64 string of encrypted data, decodes it to bytes, and then decrypts it using the same AES algorithm and key/IV. The decrypted data is then converted back to a string.

The writeFile function now encrypts the data before writing it to the file, and the readFile function decrypts the data after reading it from the file. This ensures that the data is always stored in an encrypted form, protecting it from unauthorized access.

Please note that this code does not include access controls or secure storage solutions, as those would depend on the specifics of your application and environment. It is recommended to implement these as well for additional security.

References