sqflite database opened without SQLCipher / page-level encryption

ID

dart.storage.sqflite_no_sqlcipher

Severity

low

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Storage

Language

Dart

Description

A sqflite database is opened without SQLCipher / page-level encryption. The .db file is plaintext on device and trivially exfiltrated via backup, root, or USB debugging.

Rationale

package:sqflite wraps the bundled SQLite binary, which stores data in a plain file on disk. Like Hive and SharedPreferences, the OS sandbox protects it from other apps but does NOT encrypt it. The exposure paths are the same: rooted devices, backups, forensic imaging, USB debugging on developer-mode devices.

For Flutter apps holding any authentication or user-identifiable data, this is rarely acceptable in production. The drop-in alternative is sqflite_sqlcipher, which uses SQLCipher (an AES-256-CBC page-encrypted fork of SQLite) and accepts a password: argument on openDatabase. The encryption key must itself be stored in flutter_secure_storage.

Confidence is LOW because the rule is a per-file heuristic: it fires on openDatabase(…​) in any file that doesn’t also contain a password: argument. A central DB-factory file that passes the password may legitimately not appear in callers — in that case the finding is a triage-time false positive.

The following code illustrates a vulnerable pattern detected by this rule:

class PlainSqlite {
  Future<Database> open() async {
    final path = join(await getDatabasesPath(), 'app.db');
    // VULNERABLE: sqflite database opened without SQLCipher / page-level encryption
    final db = await openDatabase(path, version: 1, onCreate: (db, v) async {
      await db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
    });
    return db;
  }
}

Remediation

Switch the import to sqflite_sqlcipher and pass a password: argument derived from a key stored in flutter_secure_storage.

// Vulnerable
import 'package:sqflite/sqflite.dart';
final db = await openDatabase('app.db', version: 1, onCreate: ...);

// Fixed
import 'package:sqflite_sqlcipher/sqflite.dart';
const storage = FlutterSecureStorage();
final password = await storage.read(key: 'db_key') ?? _provisionKey(storage);
final db = await openDatabase(
  'app.db',
  version: 1,
  password: password,
  onCreate: ...
);

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved