What is SSL Pinning
Answer
Overview
SSL Pinning (also called Certificate Pinning) is a security technique that prevents Man-in-the-Middle (MITM) attacks by ensuring your app only trusts a specific SSL certificate or public key — not just any certificate signed by a trusted CA.
The Problem SSL Pinning Solves
Without pinning, if an attacker installs a rogue root CA on the device, they can intercept HTTPS traffic by presenting a "valid" certificate. SSL Pinning prevents this by hardcoding the expected certificate.
textWithout Pinning: App → [Attacker's Proxy with fake CA] → Server App accepts the rogue certificate — traffic intercepted ❌ With Pinning: App → [Attacker's Proxy] → Server App rejects the certificate (doesn't match pinned) ✅
Types of SSL Pinning
| Type | What is Pinned | Flexibility |
|---|---|---|
| Certificate Pinning | Full certificate | ❌ Must update app when cert expires |
| Public Key Pinning | Public key only | ✅ Survives cert renewal (key stays same) |
| SPKI Hash Pinning | Hash of public key | ✅ Most modern approach |
Implementing SSL Pinning in Flutter
Method 1: Using dio + dio_pinning interceptor
dartimport 'package:dio/dio.dart'; import 'package:flutter/services.dart'; Future<Dio> createPinnedDio() async { // Load certificate from assets final ByteData data = await rootBundle.load('assets/certs/server.cer'); final Uint8List certBytes = data.buffer.asUint8List(); final SecurityContext context = SecurityContext(); context.setTrustedCertificatesBytes(certBytes); final HttpClient client = HttpClient(context: context); final adapter = IOHttpClientAdapter(); adapter.createHttpClient = () => client; final dio = Dio(); dio.httpClientAdapter = adapter; return dio; }
Method 2: Using http_certificate_pinning package
yamldependencies: http_certificate_pinning: ^4.0.0
dart// Check certificate before making request try { final response = await HttpCertificatePinning.check( serverURL: 'https://api.example.com headerHttp: {'Accept': 'application/json'}, sha: SHA.SHA256, allowedSHAFingerprints: ['YOUR_CERTIFICATE_SHA256_FINGERPRINT'], timeout: 60, ); } on PlatformException catch (e) { // Certificate doesn't match — possible MITM attack! print('Certificate pinning failed: $e'); }
Getting the Certificate Fingerprint
bash# Get SHA-256 fingerprint of a server's certificate openssl s_client -connect api.example.com:443 -servername api.example.com < /dev/null 2>/dev/null | \ openssl x509 -fingerprint -sha256 -noout # Output: SHA256 Fingerprint=AB:CD:EF:12:34:56...
Considerations
| Aspect | Detail |
|---|---|
| Certificate expiry | Pin public key (not full cert) to survive renewals |
| Backup pins | Always include at least 2 pins for rollback |
| Debugging | Must disable pinning in debug mode |
| App update | App must be updated when pinned cert changes |
| Bypass risk | Root devices / Frida can bypass pinning |
Debug vs Release
dart// Only enable pinning in release builds Future<Dio> createDio() async { final dio = Dio(); const bool isProduction = bool.fromEnvironment('dart.vm.product'); if (isProduction) { // Apply certificate pinning dio.httpClientAdapter = await _createPinnedAdapter(); } return dio; }
Key Point: SSL Pinning is an important layer of defense against MITM attacks, especially for financial or healthcare apps. Always pin the public key (not the full certificate) so you don't need to update the app when the certificate is renewed.