Introduction
Building secure mobile apps requires more than just a powerful framework like Flutter. It demands a proactive approach to mitigating ever-evolving threats. In this blog, we’ll explore various techniques and best practices for securing a Flutter application using the recently released OWASP Mobile Top 10 2024 list. We will be looking into crucial areas like credential mishandling, communication vulnerabilities, data storage pitfalls, and binary protection weaknesses and also steps to mitigate these risks along with some examples. Lets take the first step into protecting these mobile applications in a secured manner.
OWASP Top 10 Mobile Risks 2024
M1: Improper Credential Usage
In this security risk, attackers could potentially locate and exploit hardcoded credentials or exploit weaknesses due to improper credential usage.
Once these vulnerabilities are identified, an attacker can use hardcoded credentials to gain unauthorized access to sensitive functionalities of the application. They can also misuse credentials, for instance by gaining access through improperly validated or stored credentials, thereby bypassing the need for legitimate access.
Examples
Usage of Hardcoded Credentials in source code.
When application communicates with a backend application , it shares user credentials in plain text form or visibility of API Keys in plain text form.
There are still applications that still store a user’s credentials in a SharedPreference that too it can be viewed in plain text form.
Here is an example of it in use:
import 'package:shared_preferences/shared_preferences.dart';
final prefs = await SharedPreferences.getInstance();
prefs.setString('username', 'user1234');
String value = prefs.getString('username');
print(value);
Best Practices for Enhancing Credential Management Security
-
Avoid using hardcoded credentials in your application’s source code , environment / configuration files.
-
Properly handle user credentials by,
-
Encrypting credentials during transmission.
-
Implement stronger User authentication protocol like Biometrics , Face ID , Touch ID using packages like local_auth , biometricx .
-
Avoid storing credentials locally in the device using unencrypted SharedPreferences or Databases storing sensitive information.
-
-
If it is necessary to store any sensitive data, use well known (secure and tested) packages like flutter_secure_storage that uses Keychain based storage for iOS and Keystore for Android.
Here is an example of using flutter_secure_storage for secure credential storage:
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
await storage.write(key: 'API_KEY', value: 'XXXXXXX');
String value = await storage.read(key: 'API_KEY');
print(value);
M2: Inadequate Supply Chain Security
This security risk can happen when an attacker can manipulate application functionality by exploiting vulnerabilities in the mobile app supply chain. This can lead to unauthorized data access or manipulation, denial of service, or complete takeover of the mobile app or device.
Examples
An attacker can perform unauthorized code execution.
Modify the code during the build process to introduce malicious code.
Exploiting vulnerabilities in third-party software libraries, SDKs or vendors.
Best Practices for Strengthening Flutter Supply Chain Security
Implement secure coding practices, code review, and testing throughout the mobile app development lifecycle to identify and mitigate vulnerabilities.
Ensure secure app signing and distribution processes to prevent attackers from signing and distributing malicious code. Refer to the Flutter Documentation on how to properly sign your application for different OS.
Always use latest and stable Flutter SDK , the most updated version of your libraries, and to regularly check security guidelines by the Flutter team and the mobile development community in general to reduce the risk of vulnerabilities.
M3: Insecure Authentication/Authorization
This security risk involves the use of weak authentication and authorization methods that can be easily exploited by attackers to gain unauthorized access to the application, its data, backend services.
Examples
Anonymous Requests without Authentication.
Applications that has Weak Password Policy like
Lack of Minimum Password Length and Complexity Requirements.
Allowing users to have common passwords that are available publicly.
Poor session management practices includes
Storing Session IDs in Client-Side Storage with world readable permission.
Generating sequential or easily guessable session IDs makes them susceptible to brute-force or prediction attacks.
Failing to invalidate sessions on logouts.
Provision of functionalities without checking roles.
Best Practices for Improving Authentication Security
- Avoid client-side authentication and reinforce using server side authentication
- Implement strong authentication techniques like OAuth (Open Authorization) using firebase_auth that provides users to authenticate using Google , Apple and other methods.
Here is an example of it in use:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
void main() async {
await initializeFirebaseApp();
await signInWithGoogle();
}
Future initializeFirebaseApp() async {
await Firebase.initializeApp();
}
Future signInWithGoogle() async {
try {
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
....
} catch (e) {
....
}
}
Implement Strong Password Policies (
firebase_auth
provides this functionality)For issues like Credentials Bruteforcing, implement limit on failed login attempts
Best Practices for Improving Authorization Security
Independently verify the roles and permissions of the authenticated user in the backend system.
Handle unauthorised requests in a secured way.
M4: Insufficient Input/Output Validation
Every mobile application works based on user’s input. If an user’s input isn’t validated and sanitized it could lead to serious vulnerabilities like SQL / Command Injection , XSS and if these attacks chained with other vulnerabilities could lead Unauthorized Access of data , Memory corruption and potentially could compromise the entire mobile system.
Examples
Injection Attacks:
SQL Injection: Malicious SQL queries can be injected via user input, potentially leading to unauthorized data access, modification, or deletion.
Command Injection: Injected commands can execute arbitrary code on the server, compromising its security and stability.
Cross-Site Scripting (XSS): Malicious scripts can be injected into output, potentially stealing user credentials or redirecting them to harmful websites.
Remote Code Execution (RCE): Unvalidated user input, especially in network requests or local file operations, can be used to execute arbitrary code on the device.
Improper Error Handling: Insufficient error handling can inadvertently disclose sensitive information or reveal vulnerabilities in error messages.
Memory Corruption: Malicious input can lead to buffer overflows or other memory corruption issues, potentially crashing the app or enabling attackers to gain control.
Best Practices for Robust Input and Output Validation
Input Validation:
Validate and sanitize user input using strict validation techniques. Leverage available libraries like validators to streamline and automate validation tasks.
Here is an example of it in use:
import 'package:validators/sanitizers.dart';
import 'package:validators/validators.dart';
print(isEmail("sampletext")); // Prints False
print(isJSON('{"name":"John", "age":30, "car":null}')); // Prints True
print(blacklist("This is a sample text used for testing.", "t")); //Prints This is a sample ex used for testing.
Implement input length restrictions and reject unexpected or malicious data.
Output Sanitization
Show necessary information regarding any error instead of providing entire error log.
Properly sanitize output data to prevent cross-site scripting attacks.
Secure Coding Practices like using prepared statements for SQL.
Here is an example of it in use:
import 'package:sqflite/sqflite.dart';
Database db = await openDatabase('database.db');
// Paramterized Query
String sql = 'INSERT INTO students (name,department) VALUES (?, ?)';
List args = [value1, value2];
await db.rawInsert(sql, args);
// Paramterized Query
sql = 'SELECT * FROM students WHERE name = ?';
List
Perform security tests regularly.
M5: Insecure Communication
In the majority of mobile applications, data typically traverses through a client-server setup. As information travels across the mobile device’s carrier network and the internet, there is a potential risk of attackers exploiting vulnerabilities, which could result in the exposure of private data and compromise the integrity of transmitted information.
Examples
Lack of certificate inspection like using `HttpURLConnection` directly without proper certificate validation in Android and Setting `allowsAnyHTTPSCertificate` to `true` on `NSURLSession` in iOS which means the app accepts any server certificate, regardless of its legitimacy.
Not using SSL/TLS Channels while transmission.
Using weak or deprecated ciphers in SSL/TLS connections makes them vulnerable to decryption attacks.
Best Practices for Securing Communication in Flutter Apps
Apply SSL/TLS to transport channels that the mobile app will use to transmit data to a backend API or web service.
Use certificates signed by a trusted CA provider.
Only establish a secure connection after verifying the identity of the endpoint server using trusted certificates in the key chain.
Consider certificate pinning. (Use http_certificate_pinning for certificate pinning in Flutter)
Here is an example of it in use:
import 'package:http_certificate_pinning/secure_http_client.dart';
SecureHttpClient getClient(List allowedSHAFingerprints){
final secureClient = SecureHttpClient.build(certificateSHA256Fingerprints);
return secureClient;
}
myRepositoryMethod(){
secureClient.get("sampleurl.com");
}
M6: Inadequate Privacy Controls
This risk is about the protecting Personally Identifiable Information (PII) like names and addresses, credit card information, e-mail , information about health, religion, sexuality and political opinions.
Attackers could leak , manipulate or destroy these information causing harm to those involved.
This security risk could happen through
Insecure data storage and communication (M5, M9).
Unauthorized data access due to insecure authentication and authorization (M3, M1).
Attacks on the app’s sandbox (M2, M4, M8).
Example
Gathering more PII than absolutely necessary for essential app functionality.
Storing PII in plain text, insecure locations, or without proper encryption leaving it vulnerable to unauthorized access.
A vague or misleading privacy policy fails to inform users about data collection, usage, and sharing practices, hindering informed consent.
Best Practices for Implementing Effective Privacy Controls
Minimize the amount of personal information that the application collect.
Implement fine-grained access controls based on user roles, permissions, and the principle of least privilege.
Collected information should be stored or transferred unless absolutely necessary. If it is necessary, the information must be stored or transmitted securely with proper authentication and authorization (as discussed in previous risks).
M7: Insufficient Binary Protection
This risk could be divided into 2 parts
Reverse Engineering : An attacker will typically download the targeted app from an app store and analyze it within their own local environment using a suite of different tools and could collect commercial API keys or hardcoded cryptographic secrets that an attacker could misuse or could reverse engineer the application to discover vulnerabilities and exploit them.
Code Tampering : Attackers could also manipulate app binaries to access paid features for free or to bypass other security checks. Thus allowing attackers to modify the application and distribute the modified application with malicious code back the users.
Examples
Applications that doesn’t obfuscate their code are vulnerable for source code analysis. This is more prevalent in Flutter application that releases their APK in Debug mode where the Dart source code could be easily be extracted from
kernel blob.bin
.Overcoming security checks for Mods and getting unauthorized access to premium features.
Repackaging the application with malicious features.
Best Practices for Applying Binary Protection Techniques
Application should check runtime whether its integrity has been compromised or not.
Publish Release versions of Flutter application. (Release version of Flutter is heavily stripped and obfuscated making it difficult for reverse engineering.)
M8: Security Misconfiguration
Security misconfiguration in mobile apps refers to the improper configuration of security settings, permissions, and controls that can lead to vulnerabilities and unauthorized access.
Examples
Requesting for Unnecessary permissions like
In Android, Granting
READ_EXTERNAL_STORAGE
orWRITE_EXTERNAL_STORAGE
when internal storage or scoped storage mechanisms are appropriate.Overusing broad capabilities like
camera
orphotos
when fine-grained permissions likecameraCaptureMedia
orphotoLibraryAdd
are available.
Transmitting sensitive data over
http
instead ofhttps
.Unprotected storage mechanism like using Shared Preferences or using storage location which has world readable permissions.
Best Practices for Avoiding Security Misconfigurations
Secure network configuration by,
In AndroidManifext.xml , configure
cleartextTrafficPermitted
tofalse
to disallow HTTP traffic.Use the
http
ordio
packages for network requests.
Avoid requesting for overly permissive permissions. Use the
permission_handler
package to request and manage permissions. (Works for both Android and iOS Environments.)
Here is an example of it in use:
import 'package:permission_handler/permission_handler.dart';
Future _requestCameraPermission() async {
final status = await Permission.camera.request();
if (status == PermissionStatus.granted) {
print('Camera permission granted');
} else {
print('Camera permission denied');
}
Avoid keeping debug features (like logging statements) in production mode.
M9: Insecure Data Storage
With the rapid proliferation of mobile applications across diverse platforms, the handling and storage of sensitive user data emerged as a focal point for security vulnerabilities. If sensitive information stored on mobile devices isn’t properly protected, it opens the door to all sorts of trouble. From unauthorized access to risking privacy and even putting organizations in jeopardy, the stakes are high.
Examples
Storing passwords, access tokens, or other sensitive credentials directly in code, shared preferences, or local files without encryption.
Caching sensitive data (e.g., user profiles, API responses) without proper access controls or encryption, both in memory and on disk.
Logging sensitive information (e.g., debug logs, network requests) to plain text files or console logs accessible to users or attackers.
Improper handling of File Permissions and API’s.
Best Practices for Protecting Data Storage in Flutter
Utilize the Android Keystore or KeyChain for hardware-backed storage of encryption keys and sensitive data.
Implement fine-grained access controls based on user roles and permissions.
Limit the amount of sensitive information logged.
Employ encryption methods on locally stored files for higher security. (Use packages like hive , flutter_secure_storage for storing data in encrypted files.)
Here is an example of using hive for protecting data storage:
import 'package:hive/hive.dart';
var box = Hive.box('data');
box.put('user', 'user1');
var name = box.get('user');
print(name);
M10: Insufficient Cryptography
This security risk which was placed M5 in the 2016 OWASP Top 10 List , talks about how attackers exploit insecure cryptographic methods employed in different mobile applications that can undermine the confidentiality, integrity, and authenticity of sensitive information. Their intentions is to decrypt sensitive information or to determine the cryptographic keys used.
Examples
An attacker intercepts communication between the app and a server, capturing and potentially modifying plaintext data.
Due to weak encryption keys or poor password requirements , attackers can guess these data using automated tools.
Vulnerabilities in cryptographic libraries or custom implementations can create exploitable weaknesses.
Weak key management practices like storing keys in plain text, hardcoding them in the app, or using predictable key generation can compromise data security.
Best Practices for Utilizing Strong Cryptographic Methods
Stop using outdated cryptographic algorithms . (Leverage popular cryptographic packages like encrypt , crypto , pointycastle )
Here is an example of it in use:
import 'package:encrypt/encrypt.dart';
void main() {
final plainT = 'This is a sample text';
final key = Key.fromUtf8('some_secure_key');
final iv = IV.fromLength(16);
final encryptObj = Encrypter(AES(key));
final encrypted = encryptObj.encrypt(plainText, iv: iv);
final decrypted = encryptObj.decrypt(encrypted, iv: iv);
print(decrypted);
}
Store Cryptographic keys in a secured manner.
Utilize the Android Keystore or KeyChain for hardware-backed key storage.
Use key derivation functions (KDFs) like PBKDF2 for password-based key generation.
Rotate keys regularly to reduce the attack surface.
Conduct regular security testing. Integrate static code analysis tools like Dart analyzer or linters (e.g.,
flutter_analyze
) to identify potential cryptographic vulnerabilities early in development. Refer this documentation to learn more aboutflutter_analyze
.Use Strong Hash Functions with KDFs like PBKDF2 or Argon2 to increase computational cost and resist rainbow table attacks. (For Argon2 implementation , use dargon2_flutter library. )
Conclusion
Wrapping up our post about making Flutter apps safer, based on the OWASP Mobile Top 10 for 2024, here’s a simpler takeaway: Keeping our apps safe is super important, and Flutter provides us various libraries and APIs’ to help with it. It’s all about making sure we’re doing the basics right, like keeping an eye on who’s accessing what, making our code tough to tamper, and making sure private stuff stays private. By applying the security measures we’ve discussed, you are not just protecting your apps, but also building trust with your users.
Looking to elevate your expertise in Mobile Application Security?
Practical Mobile Application Exploitation Training
365 Days of Access | Hands-On Learning | Self-Paced Training