Flutter mobile - certificate and SSL exclusions for development domains

Flutter mobile development gets twisty when working behind corporate firewalls and with self-signed development certificates. We look at a bit of code that can be used in debug builds that sets the emulator/simulator proxies and disables cert checks.

 

Example Code

See fs-dart-tools on GitHub for the latest versions of this code.

You can use this sample code wrapped in an `if (isKDebug)` when running Flutter on mobile.  It lets you exclude your self-signed certificates for your internal domains while leaving certificate checks enabled for production and external domains.  The code also sets the proxy when running in the Android Emulator or the iOS Simulator.  Setting the proxy is optional, enabled when you pass in the proxy port.  


import 'dart:io';

///
/// Ignores certificates in developermode for limited set of domains
/// Simplifies development in corporate self signed environment
/// Configures the emulator or simulator proxy if passed proxy port.
/// Assumes there is a proxy running locally if proxy port provided
/// Proxy only used for mobile devices. We assume browser is already configured.
///
class DevelopmentHttpConfig extends HttpOverrides {
  DevelopmentHttpConfig({
    required this.certExclusionDomains,
    this.iosProxyHost = '127.0.0.1',
    this.androidProxyHost = '10.0.2.2',
    this.proxyPort,
  });

  /// Proxy used for android
  /// Defaults to the 10.0.2.x network
  String androidProxyHost;

  /// Proxy used for IOS because it uses the host network. defaults to 127.0.0.1
  /// Android has its own network
  String iosProxyHost;

  /// list of domains we exclude from cert check
  List<String> certExclusionDomains;

  /// proxyPort != null means setup the proxy if isAndroid or isIOS
  int? proxyPort;

  @override
  HttpClient createHttpClient(SecurityContext? context) {
    final exclusion = super.createHttpClient(context);

    // ignore self signed certs in these domains
    exclusion.badCertificateCallback = (cert, host, port) =>
        certExclusionDomains.any((element) => host.endsWith(element));

    if (proxyPort != null) {
      if (Platform.isAndroid) {
        exclusion.findProxy = (url) => 'PROXY $androidProxyHost:$proxyPort';
      }
      if (Platform.isIOS) {
        exclusion.findProxy = (url) => 'Proxy $iosProxyHost:$proxyPort';
      }
    }
    return exclusion;
  }
}


I've worked in corporate environments where the Android 10.0.2.1 doesn't work for reaching sites and I have to use the corporate proxy instead.


Example usage from test case


Here are two test cases. "wrong.host.badssl.com" is a test site with a bad certificate.  We test connecting to it with that domain in our "do not check list" and again with it not in the list.


test('Succeed with an exclusion', () async {
      const badSSLDomain = 'wrong.host.badssl.com';
      const requestDomain = 'wrong.host.badssl.com';
      HttpOverrides.global = DevelopmentHttpConfig(
        certExclusionDomains: [badSSLDomain], /*proxyPort: 8080*/
      );

      final url = Uri.https(requestDomain, '/');
      final response = await http.get(url);
      expect(response.statusCode, equals(HttpStatus.ok));
    });

test('Fail Without exclusion', () async {
      const requestDomain = 'wrong.host.badssl.com';

      final url = Uri.https(requestDomain, '/');
      // ignore: unawaited_futures
      expectLater(() => http.get(url), throwsException);
    });


Video


A quick walkthrough of the code.

Revision History

Created 2024 01


Comments

Popular posts from this blog

Understanding your WSL2 RAM and swap - Changing the default 50%-25%

Installing the RNDIS driver on Windows 11 to use USB Raspberry Pi as network attached

DNS for Azure Point to Site (P2S) VPN - getting the internal IPs