July 25, 2023

    Flutter Mobile Application Protection: Dos and Don’ts

    There are many excellent reasons to use Flutter to build your apps. With the effort and resources required to launch your mobile app, it is important to ensure you are not compromising the security of your application. In the following blog, we will provide recommendations for you to achieve a well-protected Flutter app.

    There are many reasons why the code of mobile Flutter apps needs to be protected:

    A basic, but important, question to ask yourself is, what is your or your organization’s risk exposure if an attacker were able to read your code, reverse engineer it and modify it?

    As an app developer, it is important to understand the difference between very simple protection that may temporarily deter an attacker versus a robust solution that will thwart an attacker and go beyond the resources and capabilities of your average attacker.

    Embedding protection into the app

    diagram-of-dos-and-donts_1Protecting a Flutter application by adding a protection layer “on top” or wrapping the existing application code without substantial changes may deter a casual attacker but will offer little resistance to a dedicated attack and leaves your app vulnerable to threat actors. Why? The simplicity of the solution equates to the simplicity of how to defeat it. Wrapper-based solutions are very compartmentalized and are often delivered as a library of protection functions. This results in easily detected “interface” points that can be readily removed and have all the protection disabled. Hence the app can then be easily extracted and modded.

    Even worse, the knowledge gained to defeat the protection in one app can be easily applied to other apps using the same wrapper solution. Done once, it can be easily replicated on other apps from different publishers.

    Wrapper solutions address some market needs beyond just being easy to implement. They are easily compatible with different technologies, making them easy to deploy when new development technologies, like Flutter, are available. Being somewhat technology agnostic, they can support different development languages and platforms like Java, Kotlin, Javascript, Java, or Flutter. Wrapper solutions can be quickly applied at the end of the app development without significantly impacting the application code. Some wrapper solutions result in increases in startup time and increased memory requirements.

    More resilient and effective protection options are compiler-based solutions that modify the application code. Compiler solutions modify the application code through a series of obfuscation techniques to encrypt strings, obfuscate operations and control flow, and even automatically inject a series of runtime checks and protections. The multilayered nature of the protection makes it extremely difficult for an attacker to reverse engineer and tamper with the app. Even if they are sophisticated enough to defeat one protection layer, they have no insight into how many additional and different protection layers are in place and have no better understanding of how to defeat them. Some products also offer polymorphic solutions, where the locations and details of the protection are changed with every build. This effectively wipes out any progress an attacker may have made in understanding the app code with every app release.

    Compiler solutions are more robust and provide a greater depth of protection. Some may perceive them to be more complex to implement; however, that is not necessarily true. The integration effort depends upon the specific tools and the application architecture. With a compiler-based solution, your app protection is tightly coupled with your actual codebase, making it difficult to be defeated by any attacker and a better choice for app protection.

    Protecting the Flutter engine

    diagram-of-dos-and-donts_2

    An app built on Flutter consists of both the application code and the Flutter engine, also referred to as the Flutter runtime code. It is not sufficient to protect just the application code. You need to protect the Flutter engine and the links between the application code and the engine as well. Otherwise, it can make it easy for attackers to compromise your app.

    Replacing the Flutter engine in a protected application is a well-known attack technique (e.g. reFlutter project on GitHub) that enables reverse engineers to control the execution of the protected application through a modified engine. For example, this could enable an attacker to intercept networking functionality or traffic to determine how and when the application communicates with the backend and substitute the traffic with a modified version.

    The key insight here is that it is not sufficient to protect just the application code; you need to protect the Flutter engine as well.

    Runtime protection is as important as static protection

    Comprehensive application protection requires both static and dynamic protection. Attackers do not limit themselves to statically analyzing applications. They also use various tools to analyze and modify the application during runtime. This is why it is important also to use RASP solutions that detect and deter attackers from dynamically analyzing the code at runtime to get around the encryption and obfuscation techniques added to the code.

    Using Flutter is not the same as app protection

    diagram-of-dos-and-donts_3Flutter is a solution to help developers develop apps for multiple platforms using a common code base. It only offers code obfuscation which, by itself, is not code protection. This is best understood by looking at examples of what code obfuscation does. Name obfuscation is the simplest obfuscation technique. It changes the names of identifiers in a program, making them hard to understand what they represent.

    Here’s an example of how it might look like to a reverse engineer:

    diagram-of-code-dos-and-donts_1

    Can you guess what this function does? Although all names are completely hidden, we may reasonably guess that the `secret2()` function is checking for a condition, and if the check is false, the application offers the user to purchase the full product.

    It would be a logical first step on the reverse engineer’s behalf to try to substitute the `secret2()` function to always return a `true` value.

    Let’s compare it with how a more sophisticated obfuscation of the same function might look like:

    diagram-of-code-dos-and-donts_2

    Note that when we add more complex obfuscation techniques, like code, arithmetic, and control flow obfuscation, the function is much more difficult to read and understand than the first one. However, even this level of obfuscation is not enough. Reverse engineers will typically use runtime debugger tools to carry out dynamic code analysis. As the engineer reads through the function, they would launch the application and set a few breakpoints to better understand the function's operation. Although they might require a lot more effort and time, such runtime techniques can be effective at understanding the code operation. That is why mobile applications need RASP (Runtime Application Self-Protection) in addition to static techniques to properly protect themselves. RASP uses a series of runtime checks to verify code and data integrity, as well as whether the application is running on a compromised environment. Even the highly robust RASP techniques can be made even more effective using Polymorphism. A capability that changes how the RASP are added with each and every build, effectively wiping out any accumulated information an attacker may have accumulated.

    How can Guardsquare help?

    As we have seen, simply using Flutter will not protect your app. You need to add more significant code hardening capabilities to properly protect your mobile application against modern reverse engineers and modders.

    Guardsquare provides a comprehensive collection of products for protecting, testing and monitoring your mobile Flutter app without significantly adding to a development team's work. The solutions are readily integrated into standard IDE and DevOp flows so they can be applied with little effort by developers. In addition, we also offer a free Mobile Application Security Testing product that will analyze your code for vulnerabilities and provide actionable recommendations on how to address the security risks.

    For Android and iOS platforms, DexGuard and iXGuard, protect your Flutter apps against tampering through multiple layers of code hardening and Runtime Application Self-Protection (RASP) checks.

    AppSweep is Guardsquare's Mobile Application Security Testing product, which is free to use, is focused on mobile security, and can be easily integrated into your DevOps flow. Its findings include the OWASP MASVS categories, which draw on the expertise of a large community of security researchers.

    Our real-time monitoring solution, ThreatCast, provides real-time insights into different types of threats mobile apps face, including debugging and hooking tools, repackaging attempts, escalation of privilege, emulators, virtual environments, and many more once the app is out in the market. This enables proactive analysis of attack attempts and adjustments to security strategy.

    This portfolio of products ensures that your Flutter mobile app has the most effective code protection solution.

    Discover how Guardsquare provides industry-leading protection for mobile apps.

    Request Pricing

    Other posts you might be interested in