How to secure your Flutter app

A detailed strategy on how to improve security in your Flutter app with the help of OWASP.

Lorenzo Greco 🌍🇺🇦
11 min readApr 14, 2023
Photo by James Sutton

Securing a mobile app is a challenge many of us mobile engineers will face at least once in our careers. It’s not always easy to ensure that things are appropriately secured if we never had the chance to dive deep into low-level security stuff or if we never had one of our apps reviewed by a third-party company that performed professional penetration tests.

I’ve been there in the past, but now, after having worked on different fintech apps and having passed quite successfully a few penetration tests, I decided to share my experience here with the hope of helping more engineers to secure their apps.

🔲 Security is not black or white

Before I start listing a few things that you should consider when you want to secure your Flutter app, I want to highlight that security must be seen as any other part of the development process, there are things that are an absolute must-do in every case and some others that need to be weighed depending on external factors, like business needs, the real benefit brought, the effort required to be compliant and so on.

🚀 Let’s start

Whenever I start something that looks like a checklist to fill out, I first try to come up with the list that I’m looking for. In this case, we can reference the top 10 mobile risks list from OWASP.

For the ones of you who don’t know what OWASP is, I’d recommend checking their website, but if you want the long story short:

The Open Worldwide Application Security Project® (OWASP) is a nonprofit foundation that works to improve the security of software….it is the source for developers and technologists to secure the web.

In this article, I’ll be listing the top 10 mobile app risks and for each of them, I’ll give some info on how they should be addressed in your Flutter app.

🛠️ M1: Improper Platform Usage

This category covers misuse of a platform feature or failure to use platform security controls. It might include misuse of:

  • platform permissions,
  • TouchID,
  • Keychain,
  • Android intents,
  • some other security control that is part of the mobile operating system.

What to do:

  • Ask the user only for permissions you really need: don’t follow a “just in case” approach and only ask for the right permissions for your app, there are multiple reasons why we, as developers, are restricted in what we can do, security is one of those.
  • Don’t rely only on TouchID local authentication: in some cases, local authentication could be enough, and you can use local_auth to implement that. But if you want a higher level of security when authenticating using biometrics, you should implement a verification process based on Asymmetric Keys (more details here). I’m not going to enter the specifics of the implementation, but what you would need in case you go down this route, are packages like flutter_biometrics or biometricx.
  • Use KeyChain/Keystore to store sensitive data: they are secure storage facilities for both app and system data. Apps should use them to store any small data with security significance (session keys, passwords, device enrolment data, etc.). A common mistake is to store such items in the app's local storage. flutter_secure_storage provides API to store data in secure storage. Keychain is used in iOS, and KeyStore-based solution is used in Android.
  • Stay up-to-date: new vulnerabilities are discovered every day. You should always make sure to use the latest Flutter stable, the most updated version of your libraries, and to regularly check security guidelines by the Flutter team and the mobile development community in general. Also, restricting your app to an higher min SDK level, will put you in a better place in terms of securtity, but be careful when you decide to cut out the compatibility with some of the older OS versions, since the may be still relevant to your bunisses.

📦 M2: Insecure Data Storage

Insecure data storage vulnerabilities occur when development teams assume that users or malware will not have access to a mobile device’s filesystem and subsequent sensitive information stored on the device. This can result in data loss, in the best case for one user, and in the worst case for many users.

What to do:

  • Use KeyChain/Keystore to store sensitive data: as mentioned in the previous section, KeyChain/Keystore should be used to store a small amount of sensitive information.
  • Only store data if absolutely necessary: you should only store data if they are key to the functionality of your app.
  • Delete the data if they are stored only for temporary or caching purposes: when you know that the data you are storing are not needed after an operation has been performed, make sure that you also delete them.
  • Encrypt data: if you really need to store user data on the device, then make sure that those data are encrypted, especially if they are personal data. hive could be a good solution for you.
  • Hide sensitive content on multitasking view: if your app shows sensitive information, you should also think of keeping those data hidden away from prying eyes using something like secure_application.

☎️ M3: Insecure Communication

When designing a mobile application, data is commonly exchanged in a client-server fashion. When the solution transmits its data, it must traverse the mobile device’s carrier network and the internet. Threat agents might exploit vulnerabilities intercepting sensitive data while travelling across the wire.

What to do:

  • Use SSL/TLS certificates for secure transmission: quite obvious, but always worth mentioning. You must apply SSL/TLS to transport channels that the mobile app will use to transmit sensitive information, session tokens, or other sensitive data to a backend API or web service. You should also account for outside entities like third-party analytics companies, social networks, etc. by using their SSL versions when an application runs a routine via the browser/webkit. Avoid mixed SSL sessions as they may expose the user’s session ID.
  • Consider using certificate pinning: certificate pinning restricts which certificates are considered valid for a particular website, limiting risk. Instead of allowing any trusted certificate to be used, operators “pin” the certificate authority (CA) issuer(s), public keys or even end-entity certificates of their choice. If needed, It can be implemented in a Flutter app using http_certificate_pinning.
  • Apply a separate layer of encryption to any sensitive data before it is given to the SSL channel: this is in case you want to add another layer of security on top of SSL, in the event that future vulnerabilities are discovered in the SSL implementation, the encrypted data will provide a secondary defence against confidentiality violation.

👤 M4: Insecure Authentication

Poor or missing authentication schemes allow an adversary to anonymously execute functionality within the mobile app or backend server used by the mobile app. In mobile apps, users are not expected to be online at all times during their session. Mobile internet connections are much less reliable or predictable than traditional web connections. Hence, mobile apps may have uptime requirements that require offline authentication. This offline requirement can have profound ramifications on things that developers must consider when implementing mobile authentication.

What to do:

  • Avoid local authentication methods: authenticating a user locally can lead to client-side bypass vulnerabilities. If the application stores data locally, the authentication routine can be bypassed on jailbroken devices through run-time manipulation or modification of the binary. This is linked also to the misuse of biometrics authentication highlighted in section M1.
  • Refrain from using vulnerable authentication methods: you should not store passwords locally or four-digit PINs, try to always stay up to date with the most recent guidelines in terms of the authentication methods and implement the one that suits your needs the best.

🔐 M5: Insufficient Cryptography

Insecure use of cryptography is common in many mobile apps that leverage encryption. There are two fundamental ways that broken cryptography is manifested within mobile apps.

First, the mobile app may use a process behind the encryption/decryption that is fundamentally flawed and can be exploited by the adversary to decrypt sensitive data.

Second, the mobile app may implement or leverage an encryption/decryption algorithm that is weak in nature and can be directly decrypted by the adversary.

What to do:

  • Don’t use insecure and/or Deprecated Algorithms: many cryptographic algorithms and protocols should not be used because they have been shown to have significant weaknesses or are otherwise insufficient for modern security requirements. These include: RC2, MD4, MD5, SHA1. Some good algorithms you can use instead are AES, Fernet, Salsa. You may be interested in using packages like encrypt and crypto.
  • Secure developer identity: any data that may expose developer identity must be encrypted. Encrypt sensitive files like key.jks & keystore.properties with GPG. Avoid keeping track of unencrypted sensitive data in your repository.

M6: Insecure Authorisation

I assume you are already aware of the difference between authentication and authorization, but just in case you are not: Authentication is the act of identifying an individual. Authorization is the act of checking that the identified individual has the permissions necessary to perform a specific action.

If authorisation is not properly handled, a user (anonymous or verified) is able to execute over-privileged functionalities.

What to do:

  • Verify the roles and permissions of the authenticated user using only information contained in backend systems: avoid relying on any roles or permission information that comes from the mobile device itself.
  • Handle unauthorised requests: when the server returns a 401 (as an example), it could mean that the credentials that we are using are not valid anymore. We should make sure that this is properly communicated to the user and sessions are terminated where needed.

🚮 M7: Poor Code Quality

The risk comes from using the wrong API, using an API insecurely, using insecure language constructs, or some other code-level issue. Importantly: this is not a piece of code running on the server. This is a risk that captures bad code that executes on the mobile device itself.

What to do:

  • Maintain consistent coding patterns that everyone in the organization agrees upon: you can leverage the linter to enforce a recommended set of lints for Flutter apps. I would suggest using flutter_lints as a starting point and then having additional rules to enforce a consistent pattern where there is no recommended approach in a clear direction (i.e. relative/absolute imports, single/double quotes, void returns for Futures and so on..).
  • Perform static code analysis: you should ensure that static code analysis is an integrated part of your development process, it’s up to you and your team to decide what works best for you, but I strongly recommend having this integrated into your CI/CD pipeline.
  • Use automation to identify vulnerabilities: automation can help to identify buffer overflows and memory leaks through the use of third-party static analysis tools.
  • Check code quality before integrating a 3rd party library: this is a tough one because we tend to blindly trust 3rd party libraries. What we should do instead (where possible) is to check if those libraries follow the most updated security guidelines. Once integrated, they are essentially part of our codebase, so they should follow the same security standards expected from the code we write internally. Tools like snyk.io are useful to reduce the risk associated with the use of 3rd party libraries.

🥷 M8: Code Tampering

Mobile code runs within an environment that is not under the control of the organization producing the code. At the same time, there are plenty of different ways of altering the environment in which that code runs. These changes allow an adversary to tinker with the code and modify it.

Although mobile code is inherently vulnerable, it is important to ask yourself if it is worth detecting and trying to prevent unauthorized code modification. Apps written for certain business verticals (gaming for example) are much more vulnerable to the impacts of code modification than others (hospitality for example). As such, it is critical to consider the business impact before deciding whether or not to address this risk.

What to do:

  • Add a root/jailbreak detection: typically, an app that has been modified will execute within a Jailbroken or Rooted environment. As such, it is reasonable to try and detect these types of compromised environments at runtime and react accordingly (report to the server or shutdown). To mitigate the risk and prevent the app from being used on a compromised device, you could use flutter_jailbreak_detection.

🏗️ M9: Reverse Engineering

An app is said to be susceptible to reverse engineering if an attacker can do any of the following things:

  • Clearly understand the contents of a binary’s string table
  • Accurately perform cross-functional analysis
  • Derive a reasonably accurate recreation of the source code from the binary

Although most apps are susceptible to reverse engineering, it’s important to examine the potential business impact of reverse engineering when considering whether or not to mitigate this risk.

What to do:

  • Use code obfuscation: Flutter allows you to obfuscate your code, you can find more details here. One thing that is worth mentioning is that even without obfuscation, reverse engineering a Flutter is harder than native apps, but obfuscation is not a bulletproof approach to prevent reverse engineering, in fact, if we do only a release of the app without obfuscating our code, there are classical binary diffing tools such as BinDiff or Diaphora that can be used to recover the original name of functions using a previous (non-obfuscated) build. But, even if an application has always been released with the Flutter built-inobfuscateoption enabled, a reverse engineer can still use this binary diffing technique to identify common Flutter frameworks used by the application. You can find a detailed article on Reverse Engineering in Flutter at this link.

🐛 M10: Extraneous Functionality

Often, developers include hidden backdoor functionality or other internal development security controls that are not intended to be released into a production environment. For example, a developer may accidentally include a password as a comment in a hybrid app. Another example includes disabling 2-factor authentication during testing. The defining characteristic of this risk is leaving functionality enabled in the app that was not intended to be released.

What to do:

  • Check the logs: ensure nothing overly descriptive about the app or the platform is being written to the logs, or simply remove any log from the release build of the app.
  • Exclude test components from the final production build: every mock or test code should be excluded from the release build because it may expose important information about data format and app functionality.
  • Examine the app’s configuration settings to discover any hidden switches: if you use external configuration files, ensure they are properly set up before creating a release build and that they cannot be manually changed to reveal important information about your app.

🎁 Wrapping up

I hope this article has been helpful to give you some guidance on how to improve the security of your Flutter app.

Please bear in mind that this is not an exhaustive list, and you may need to follow a more/less strict approach depending on your business needs.

As mentioned at the beginning of the article, security is not black or white, all or nothing, you need to sync with your team to understand what is required and what is not for your specific case, and you should weigh pros/cons and risks for some of the cases above.

Thanks for reading 🙏

Don’t forget to clap/share/comment and follow me to explore more content about Flutter 🐦.

--

--