Insecure SSL/TLS configuration (certificate validation disabled)

ID

dart.network.insecure_ssl

Severity

critical

Remediation Complexity

trivial

Remediation Risk

medium

Remediation Effort

medium

Resource

Network

Language

Dart

Description

TLS certificate validation is unconditionally bypassed (badCertificateCallback / onBadCertificate / validateCertificate returns true always, a WebView server-trust handler proceeds unconditionally, or mixed-content / insecure-content flags are flipped on). Any man-in-the-middle with any certificate is trusted.

Rationale

Disabling TLS validation is the most common Flutter security bug. It typically appears during development ("my proxy keeps rejecting the self-signed cert!") and survives into production because the call site is unconditional. The consequence is total loss of transport security:

  • A captive-portal Wi-Fi attacker presents any cert and the app accepts it; all subsequent traffic decrypts and the attacker forges responses.

  • An installed malicious root CA on the device (typical in BYOD / spyware scenarios) gets used silently.

  • DNS spoofing combined with any cert succeeds.

Flutter does not use the OS proxy or trust store on most targets, so pinning has to happen at the Dart layer. A trust-all callback defeats this entirely and leaves no fallback. The same problem applies to WebView: onReceivedServerTrustAuthRequest returning ServerTrustAuthResponseAction.PROCEED unconditionally is the WebView equivalent of badCertificateCallback ⇒ true.

The following code illustrates a vulnerable pattern detected by this rule:

class TrustAll {
  HttpClient buildHttp() {
    final c = HttpClient();
    // VULNERABLE: Insecure SSL/TLS configuration (certificate validation disabled)
    c.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
    return c;
  }
}

Remediation

Never blanket-trust. Either:

  • Validate normally: let badCertificateCallback default (returns false) and don’t override it. The OS / library validates the chain.

  • Pin the certificate: load the expected leaf or root certificate bytes into a SecurityContext(withTrustedRoots: false) via setTrustedCertificatesBytes. For Dio, configure the same context via IOHttpClientAdapter. For WebView, inspect the server cert in the trust callback and return CANCEL on any mismatch.

// Vulnerable
HttpClient()..badCertificateCallback = (_, __, ___) => true;
IOHttpClientAdapter(validateCertificate: (cert, host, port) => true);

// Fixed: pin a known root / leaf certificate
final ctx = SecurityContext(withTrustedRoots: false)
  ..setTrustedCertificatesBytes(pinnedRootCaBytes);
final client = HttpClient(context: ctx);

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved