Written by: Tornike Kurdadze | Sat Nov 23 2024 Flutter Security

Introduction

Security breaches can destroy user trust and your app’s reputation. This guide shows you practical steps to protect your Flutter apps and user data, using real examples from production apps.

1. Essential Security Features in Flutter

Why Flutter is Secure By Default

  • Type Safety: Catches coding mistakes early
// This won't compile - Flutter prevents this error
String name = 42; // Error: A value of type 'int' can't be assigned to a variable of type 'String'

// This is safe
String name = "John";
  • Memory Protection: Prevents common memory attacks
// Flutter automatically handles memory
// You don't need to manually allocate/free memory like in C++
void someFunction() {
var list = List.filled(1000000, "test");
// Memory is automatically cleaned when not needed
}
  • Compiled Code: Makes your app harder to hack
# In build.gradle, enable R8 for better code protection
android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
        }
    }
}

2. Real Security Problems and Solutions

Problem 1: Storing Sensitive Data

class SecureStorage {
  final storage = FlutterSecureStorage();

  // Bad - Don't do this
  Future<void> unsafeStore(String creditCard) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('credit_card', creditCard); // Stored in plain text!
  }

  // Good - Do this instead
  Future<void> safeStore(String creditCard) async {
    await storage.write(
      key: 'credit_card',
      value: creditCard,
      aOptions: AndroidOptions(
        encryptedSharedPreferences: true,
        keyCipher: StorageCipher.AES_CBC_PKCS7Padding,
      ),
      iOptions: IOSOptions(
        accessibility: IOSAccessibility.first_unlock,
      ),
    );
  }
}

Problem 2: Unsafe Network Calls

class ApiService {
  late Dio _dio;

  ApiService() {
    _dio = Dio()
      ..options.baseUrl = 'https://api.yourapp.com'
      ..options.connectTimeout = Duration(seconds: 5)
      ..options.receiveTimeout = Duration(seconds: 3)
      ..interceptors.add(
        InterceptorsWrapper(
          onRequest: (options, handler) {
            // Add security headers
            options.headers['X-API-Key'] = const String.fromEnvironment('API_KEY');
            return handler.next(options);
          },
          onError: (error, handler) {
            // Handle errors securely
            if (error.response?.statusCode == 401) {
              // Handle unauthorized access
              logout();
            }
            return handler.next(error);
          },
        ),
      );
  }

  // Example secure API call
  Future<UserData> fetchUserData(String userId) async {
    try {
      final response = await _dio.get('/users/$userId');
      return UserData.fromJson(response.data);
    } catch (e) {
      // Log error securely
      await SecureLogger.logError(e);
      throw SecurityException('Failed to fetch user data');
    }
  }
}

Problem 3: User Authentication

class SecureAuth {
  // Check if device is secure
  Future<bool> isDeviceSecure() async {
    final localAuth = LocalAuthentication();
    return await localAuth.canCheckBiometrics || 
           await localAuth.isDeviceSupported();
  }

  // Secure login with biometrics
  Future<bool> authenticateUser() async {
    try {
      final localAuth = LocalAuthentication();
      return await localAuth.authenticate(
        localizedReason: 'Please authenticate to access the app',
        options: const AuthenticationOptions(
          biometricOnly: true,
          stickyAuth: true,
        ),
      );
    } catch (e) {
      await SecureLogger.logError(e);
      return false;
    }
  }

  // Secure session management
  Future<void> startSecureSession() async {
    final sessionToken = await generateSecureToken();
    await SecureStorage().safeStore('session_token', sessionToken);
    
    // Start session timeout
    Timer(Duration(minutes: 30), () {
      logout();
    });
  }
}

3. Protecting Against Common Attacks

SQL Injection Protection

class DatabaseService {
  // Bad - Vulnerable to SQL injection
  Future<void> unsafeQuery(String userInput) async {
    await db.rawQuery("SELECT * FROM users WHERE name = '$userInput'");
  }

  // Good - Safe from SQL injection
  Future<void> safeQuery(String userInput) async {
    await db.query(
      'users',
      where: 'name = ?',
      whereArgs: [userInput],
    );
  }
}

XSS Protection

class HtmlSanitizer {
  static String sanitize(String html) {
    // Remove dangerous HTML tags and attributes
    return html
        .replaceAll(RegExp(r'<script[^>]*>.*?</script>'), '')
        .replaceAll(RegExp(r'on\w+=".*?"'), '')
        .replaceAll(RegExp(r'javascript:'), '');
  }
}

4. Security Testing

Basic Security Tests

void main() {
  group('Security Tests', () {
    test('should securely store sensitive data', () async {
      final storage = SecureStorage();
      await storage.safeStore('test_key', 'sensitive_data');
      
      // Verify data is encrypted
      final file = File('path_to_storage');
      final content = await file.readAsString();
      expect(content.contains('sensitive_data'), false);
    });

    test('should prevent unauthorized access', () async {
      final auth = SecureAuth();
      final result = await auth.authenticateUser();
      
      // Verify authentication is required
      expect(result, false);
    });
  });
}

5. Security Checklist for Production

Before Release

  1. ✅ Enable code obfuscation
  2. ✅ Implement certificate pinning
  3. ✅ Set up crash reporting
  4. ✅ Add API key protection
  5. ✅ Test on real devices

Regular Maintenance

  1. 🔄 Update dependencies monthly
  2. 🔄 Review security logs
  3. 🔄 Test security features
  4. 🔄 Update encryption keys
  5. 🔄 Audit user permissions

Conclusion

Remember:

  • Security is ongoing - keep updating and testing
  • Use Flutter’s built-in security features
  • Always encrypt sensitive data
  • Test security features regularly
  • Keep dependencies updated

A secure app is a successful app. Take security seriously from day one.

Coffee makes me feel like I have my shit together 😀, so here you go 💙