Building Secure Mobile App with Flutter
The only guide you need
Mobile applications seem to be safe by default, but are they really?
There are multiple aspects to consider when building a secure mobile app. In this article, I will cover the most important ones in Flutter development.
Sensitive Data Storage
Don’t Store Sensitive Data
The simplest way to keep data safe is not to store it at all.
Do not ask for user information that you do not need, because if you do not have it, it cannot be leaked.
Ask for minimal permissions, minimal required data to run the app.
Use Secure Storage
If you need to store some data, use secure storage solutions.
Key-value pairs can be stored in the flutter_secure_storage package, which uses Keychain on iOS and AES encryption on Android.
If you need to store more complex data in a database, use a database with supported encryption. Here are a couple of examples (not an exhaustive list):
To store the encryption key, use the secure method mentioned above. This way, the key is securely stored and the data is encrypted.
If you seek to encrypt the data itself, use libraries like pointycastle.
Use and Forget Principle
The data which you do not need should not be in any global state or in memory.
If you are handling API key or token, store it in secure storage and load it only when needed. Use it for the request and remove it.
You might ask yourself why?
Couple of reasons:
- It decreases the time window during which the sensitive data is in memory and can be leaked.
- If your app is compromised, the attacker will not be able to get the data from memory.
- It decreases the chance of accidental leak, for example by logging the state.
No .env Files with Secrets
Never store secrets in .env files or any other configuration files.
Never store secrets in the code itself.
There are plenty of articles about how to hide secrets in the code, but the truth is, if the app is decompiled, the secrets can be found.
Store the secrets on the server side and use authentication methods to access these capabilities.
API keys in the GoogleServices-Info.plist or google-services.json files for Firebase are not considered secrets because they are embedded in the client app. However, make sure all the Google Cloud APIs that your app accesses are restricted to your app’s bundle ID and certificate fingerprint. Moreover, Firebase uses security rules to protect your data and resources, so make sure to configure them properly.
Secure Network Communication
Always use HTTPS for network communication.
It is fine to use HTTP for local development, but production apps should always use HTTPS. No plain text communication should be allowed.
Certificate Pinning
If you want to be extra secure, use certificate pinning.
It ensures that the app communicates only with trusted servers, so no middleman can intercept the communication and divert it to another server.
On Android, you can achieve it by using a network security config.
On iOS, you can use identity pinning by modifying .plist file. Here is a good article about certificate pinning in iOS.
Logging
Ideally, the app should not log anything to the console, but that is not always possible.
Sometimes, it is the only way to find out what is going on.
However, you should never log sensitive data like tokens, API keys, personal information, etc.
When you are writing logs, stay with vague information like “User logged in” or “Data fetched successfully” - not revealing any sensitive information.
Otherwise, all the logs can be viewed by anyone who has access to the device or the log files.
By the way, I wrote a whole article about logging in Flutter if you want to learn more.
Most of the logging packages contain log levels, so you can set the log level to error or warning in production and debug in development. Some apps do not log anything in production at all.
Obfuscation
Obfuscation is a technique that makes the code harder to understand.
It is not a security measure by itself, but it makes it harder for attackers to reverse engineer the app and find vulnerabilities.
In Flutter, you can enable obfuscation by adding the following flags to your build command:
flutter build <build-target> --obfuscate --split-debug-info=<symbols-directory>
On Android, you can also use ProGuard or R8 to shrink and obfuscate the code.
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Packages
This is my personal favorite topic.
When you are using third party packages, you are trusting the package maintainer that the package is secure and does not contain any vulnerabilities.
I wrote a whole article about how to use reasonable dependencies if you want to learn more.
But in nutshell, my advice is:
- Try to avoid adding dependencies as much as possible, and implement the functionality on your own if it is not too complex.
- If you need to add a dependency, check the package’s popularity, maintenance, issues, and last update.
- If you are using a package, check the changelog and test it locally before updating to a new version.
- AI can help you to implement small functionalities on your own, so you do not need to add a dependency for every little thing.
Conclusion
Many companies do not care about security, but you should.
It is easy to implement these measures, and it will save you a lot of trouble in the future.
There are a couple of companies who specialize in mobile app security, so if you want to be extra sure, you can hire them to do a security audit of your app, but if you follow the best practices mentioned above, you should be fine.
There will be always new vulnerabilities and new ways to attack the app, so stay updated and keep learning.
Socials
Thanks for reading this article!
For more content like this, follow me here or on X or LinkedIn.