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 Cryptography.


Majid Hajian Azure & AI advocate @Microsoft Dart & Flutter community leader Organizer @FlutterVikings Author of http://flutterengineering.io
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 2005. 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 Cryptography covers:
weak algorithms
poor key management
improper implementations
insecure random number generation
Let’s get into it.
Source code: All code examples from this article are available as a runnable Flutter project on GitHub:
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.
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:
Weak or deprecated algorithms: MD5, SHA-1, DES, 3DES, RC4
Insufficient key lengths: AES-128 when AES-256 is required, RSA < 2048 bits
Hardcoded keys: keys embedded in source
Predictable IVs: reused IVs or sequential IVs
Insecure random generation: using
Random()instead ofRandom.secure()Missing authenticated encryption: AES-CBC without HMAC
Poor password hashing: SHA-256 instead of bcrypt / Argon2id
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.
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.
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.
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:
Never store passwords in plaintext.
Never use fast hashes for passwords (MD5/SHA-1/SHA-256).
Always use a unique salt per password.
Use Argon2id, bcrypt, or scrypt.
Argon2id (Recommended)
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()vsRandom()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?

