OWASP Top 10 For Flutter – M10: Insufficient Cryptography in Flutter & Dart

Welcome to the final article in our deep dive into the OWASP Mobile Top 10 for Flutter developers.

In earlier parts, we tackled:

Each is a critical piece of the mobile security puzzle.

In this tenth and final article, we focus on M10: Insufficient Cryptographyarrow-up-right.

Let me tell you about the scariest code review I’ve ever done.

A few years ago, I was asked to review a financial app. It handled millions of dollars in transactions. The team used encryption everywhere. They had secure storage. They’d thought about authentication.

But their crypto implementation used DES. The key was hardcoded. It was literally the word password padded to 8 bytes.

DES was considered insecure by the late 90s. NIST officially withdrew it in 2005arrow-up-right. This was in 2021.

The developers weren’t incompetent. They were talented engineers. They simply hadn’t kept up with cryptographic best practices. They copied code from a Stack Overflow answer from 2008. They assumed it was fine.

That’s why this final article exists.

OWASP M10: Insufficient Cryptographyarrow-up-right covers:

  • weak algorithms

  • poor key management

  • improper implementations

  • insecure random number generation

Let’s get into it.

book-blank

Understanding the Threat Landscape

Before we get into code, it helps to know who breaks cryptographic systems. It also helps to know how.

Who’s Interested in Breaking Your Crypto?

Crypto issues attract motivated attackers. Breaking encryption exposes everything it was meant to protect.

Who
Motivation
Attack methods

Nation-state groups

Espionage, mass surveillance

Advanced cryptanalysis, quantum computing research

Cybercriminals

Financial theft, ransomware

Brute force, known-plaintext attacks

Malicious insiders

Data exfiltration

Key theft, algorithm manipulation

Competitors

Industrial espionage

Decryption of trade secrets

Security researchers

Finding vulnerabilities

Protocol analysis, side-channel attacks

How Cryptographic Systems Get Broken

OWASP rates exploitability as AVERAGE. It’s harder than SQL injection. But it’s devastating when it works.

Attackers rarely break modern algorithms directly. They exploit implementation mistakes.

Common examples:

  • predictable random numbers

  • reused nonces

  • hardcoded keys

  • deprecated algorithms

Common Weaknesses in Flutter Apps

Here are the cryptographic weaknesses I see most often in Flutter apps:

  1. Weak or deprecated algorithms: MD5, SHA-1, DES, 3DES, RC4

  2. Insufficient key lengths: AES-128 when AES-256 is required, RSA < 2048 bits

  3. Hardcoded keys: keys embedded in source

  4. Predictable IVs: reused IVs or sequential IVs

  5. Insecure random generation: using Random() instead of Random.secure()

  6. Missing authenticated encryption: AES-CBC without HMAC

  7. Poor password hashing: SHA-256 instead of bcrypt / Argon2id

  8. Improper key storage: SharedPreferences or plain files

Each one is an opportunity for attackers. Let’s avoid them.

Algorithm Selection Guide

Cryptographic standards exist for a reason. Don’t reinvent this per project.

Symmetric Encryption

Use this for most “encrypt data” cases.

Algorithm
Key size
Use case
Status

AES-256-GCM

256-bit

General purpose, authenticated

✅ Recommended

ChaCha20-Poly1305

256-bit

Mobile/embedded, software-friendly

✅ Recommended

AES-256-CBC + HMAC

256-bit + MAC key

Legacy compatibility

⚠️ Acceptable

AES-128-GCM

128-bit

Performance critical

⚠️ Acceptable

AES-ECB

Any

Never use

❌ Insecure

DES, 3DES, RC4, Blowfish

Various

Never use

❌ Deprecated

My default is AES-256-GCM. It’s fast on modern devices. It’s widely supported. It gives integrity and confidentiality.

ChaCha20-Poly1305 is an excellent alternative. It can outperform AES on devices without AES hardware.

Asymmetric Encryption

Use this for key exchange and digital signatures.

Algorithm
Key size
Use case
Status

RSA-OAEP

≥ 2048-bit

Key wrapping, encryption

✅ Recommended

ECDH (P-256 / P-384)

256/384-bit

Key agreement

✅ Recommended

X25519

256-bit

Modern key agreement

✅ Recommended

Ed25519

256-bit

Modern signatures

✅ Recommended

ECDSA (P-256)

256-bit

Signatures

✅ Recommended

RSA-PKCS1v1.5

Any

Avoid (padding oracle risk)

⚠️ Legacy only

RSA < 2048-bit

< 2048-bit

Never use

❌ Insecure

For new projects, I lean toward X25519 and Ed25519. RSA-OAEP with 2048+ bits is still fine.

Hashing and Key Derivation

Password hashing is not general-purpose hashing.

Algorithm
Use case
Status

Argon2id

Password hashing

✅ Best

bcrypt

Password hashing

✅ Recommended

scrypt

Password hashing

✅ Recommended

PBKDF2-SHA256

Key derivation (310k+ iterations)

⚠️ Acceptable

HKDF-SHA256

Key expansion from strong key

✅ Recommended

SHA-256 / SHA-512

Integrity checks, HMAC

✅ Recommended

SHA-1

Avoid for security

❌ Deprecated

MD5

Never use

❌ Broken

Argon2id, bcrypt, and scrypt are deliberately slow. That’s a feature.

Secure Implementation in Flutter

Setting Up Cryptography Packages

Add these to pubspec.yaml:

The cryptography package is my default. It’s well maintained. It supports modern algorithms.

Symmetric Encryption with AES-GCM

This is a complete example using AES-256-GCM and secure key storage.

Using ChaCha20-Poly1305

Common Mistakes to Avoid

Password Hashing Best Practices

Rules:

  1. Never store passwords in plaintext.

  2. Never use fast hashes for passwords (MD5/SHA-1/SHA-256).

  3. Always use a unique salt per password.

  4. Use Argon2id, bcrypt, or scrypt.

PBKDF2 (When Argon2 Isn’t Available)

OWASP guidance (as of 2023) recommends 310,000+ iterations for PBKDF2-SHA256.

Key Management Best Practices

Crypto is only as strong as your key management.

Key Generation

Never use Random() for security. Use Random.secure() or let the crypto library generate keys.

Key Storage Architecture

Don’t store keys in plaintext preferences or files.

  • Android: Keystore-backed secure storage

  • iOS: Keychain-backed secure storage

Digital Signatures

Use signatures to verify authenticity and detect tampering.

Secure Random Number Generation

Random() is for UI and games. It’s not a CSPRNG.

Use Random.secure() for security.

Security Checklist

Use this to audit your crypto.

Cryptographic Implementation Audit

  • Algorithm selection

    • ☐ Use AES-256-GCM or ChaCha20-Poly1305.

    • ☐ Avoid MD5 / SHA-1 / DES / 3DES.

    • ☐ Use AEAD where possible.

  • Key management

    • ☐ Generate keys with a CSPRNG.

    • ☐ Store keys in Keychain / Keystore.

    • ☐ No hardcoded keys.

    • ☐ Rotation plan exists.

  • IV / nonce handling

    • ☐ Unique nonce per encryption.

    • ☐ Correct nonce length (12 bytes for GCM).

  • Password hashing

    • ☐ Argon2id, bcrypt, or scrypt.

    • ☐ Unique salt per password.

    • ☐ Work factor is current.

  • Implementation security

    • ☐ Vetted crypto libraries.

    • ☐ Constant-time comparisons for secrets.

    • ☐ Error handling avoids oracles.

Quick Reference: Minimum Parameters

  • AES-GCM: 256-bit key, 96-bit nonce, 128-bit tag

  • ChaCha20-Poly1305: 256-bit key, 96-bit nonce

  • RSA: 2048-bit minimum (3072-bit recommended)

  • Argon2id: 64MB memory, 3 iterations, 4 parallelism

  • PBKDF2-SHA256: 310,000+ iterations

  • Salt: 16+ bytes

Conclusion

Cryptography is powerful. It’s also easy to get wrong.

The difference between secure and insecure crypto is usually details:

  • Random.secure() vs Random()

  • AES-GCM vs AES-ECB

  • Argon2id vs SHA-256 for passwords

If you follow the patterns here and stick to well-tested libraries, you’ll avoid most real-world crypto failures.

Resources

Last updated

Was this helpful?