Hardcoded cryptographic key

ID

dart.cryptography.hardcoded_cryptographic_key

Severity

critical

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Cryptography

Language

Dart

Description

A cryptographic key is embedded directly in the application source as a string or byte array literal. The Dart obfuscator does NOT obfuscate string literals, so the key is recoverable from the shipped APK / IPA.

Rationale

Cossack Labs' review of Flutter security explicitly notes that Dart’s obfuscator (flutter build --obfuscate) preserves string literals intact — they are extractable from the Dart snapshot with analyze_snapshot or a hex editor. A hardcoded key therefore has the same trust level as a key published on GitHub: anyone with the binary has it.

Once an attacker has the symmetric key, they can decrypt all data the app ever produced with it (including ciphertext exfiltrated from backups, sync services, telemetry, or device images), forge ciphertext that the app will accept as authentic, and impersonate the user against any backend that trusts that key. For asymmetric private keys, the consequence is signature forgery — the attacker can sign anything the legitimate app could.

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

class HardcodedKeys {
  void encryptKeys() {
    // VULNERABLE: Hardcoded cryptographic key
    final k1 = Key.fromUtf8("my 32 length key................");
    // VULNERABLE: Hardcoded cryptographic key
    final k2 = Key.fromBase64("aGVsbG93b3JsZGhlbGxvd29ybGQxMjM0");
    // VULNERABLE: Hardcoded cryptographic key
    final k3 = Key.fromLength(32);
    // VULNERABLE: Hardcoded cryptographic key
    final k4 = Key(Uint8List.fromList([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]));
    print('$k1 $k2 $k3 $k4');
  }
}

Remediation

Move the key out of the bundle entirely:

  • Per-user secrets: derive at runtime from a user-supplied passphrase using a memory-hard KDF (Argon2id / scrypt / PBKDF2 >= 600 000 iterations).

  • Per-device secrets: store in flutter_secure_storage (backed by Keychain on iOS, EncryptedSharedPreferences / Keystore on Android).

  • Server-shared secrets: fetch from an authenticated backend after sign-in; never bundle them.

  • Public keys (for pinning, signature verification): hardcoding the public half is acceptable; only private / symmetric keys must be protected.

// Vulnerable
final key = enc.Key.fromUtf8('DVFA_STATIC_KEY_8899001122334455');
final p   = KeyParameter(utf8.encode('hardcoded-key-123'));

// Fixed
const storage = FlutterSecureStorage();
final stored = await storage.read(key: 'app_key');
final key = enc.Key(base64Decode(stored!));

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved