Certificate Pinning in Flutter
HTTPS protects data in transit by authenticating the server and encrypting traffic. Certificate pinning adds another trust constraint: the application expects a particular certificate or public key rather than trusting every certificate accepted by the platform's normal trust store.
Pinning can reduce the risk of certain man-in-the-middle scenarios, but it also creates an operational problem: your application must survive certificate and key rotation.
1. What pinning actually does
Without pinning:
Flutter App
↓
TLS
↓
Platform trust store
↓
Server certificate chain
With pinning:
Flutter App
↓
TLS validation
↓
Platform trust store
+
Pinned certificate/public key
↓
Server
The connection must satisfy the additional application-defined trust rule.
2. Certificate pinning vs public-key pinning
There are two common approaches.
Certificate pinning
The app stores the expected certificate.
server certificate
↓
fingerprint
↓
app compares fingerprint
The disadvantage is that certificates expire and are replaced.
Public-key pinning
The app pins a public key or its hash.
certificate
↓
public key
↓
hash
↓
pinned value
This can make rotation easier when a new certificate is issued for the same key.
3. Decide whether you really need pinning
Before adding pinning, evaluate:
- Threat model
- API sensitivity
- Device-management model
- Certificate rotation process
- Emergency recovery process
- Ability to ship an updated app quickly
Pinning is not automatically "more secure" if it causes legitimate users to lose network access whenever certificates rotate unexpectedly.
4. Do not hard-code one certificate without a rotation strategy
A dangerous design is:
App version 1
↓
Only certificate A accepted
When certificate A expires:
Server → certificate B
App → rejects B
The application can stop communicating with your backend.
A better strategy is to support a planned primary and backup pin:
Primary pin → current key
Backup pin → future/rotation key
The server can rotate certificates while the application continues to recognize the expected key.
5. Android implementation
On Android, network security configuration can be used for certificate pinning.
Conceptually:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">
api.example.com
</domain>
<pin-set>
<pin digest="SHA-256">
BASE64_PUBLIC_KEY_HASH
</pin>
<pin digest="SHA-256">
BACKUP_PUBLIC_KEY_HASH
</pin>
</pin-set>
</domain-config>
</network-security-config>
Then reference the configuration from the Android application manifest.
The exact configuration should be aligned with the Android version and security requirements of your application.
6. iOS implementation
On iOS, pinning is commonly implemented through URL loading/TLS delegate mechanisms or a networking/security library.
The key principle remains:
TLS server trust
+
application pin
=
accepted connection
Avoid implementing custom TLS validation without understanding the platform trust APIs. Incorrect trust handling can accidentally weaken security.
7. Flutter/Dio architecture
If your application uses Dio, keep security policy separate from API business logic.
A useful structure is:
Dio
|
+-- Auth interceptor
|
+-- Logging interceptor
|
+-- Retry policy
|
+-- Security/TLS configuration
|
+-- API services
Do not make every API repository responsible for TLS validation.
8. Do not ship a bypass in production
A common development pattern is:
if (kDebugMode) {
// relaxed validation
}
That can be acceptable for controlled development environments, but make sure production builds cannot accidentally enable a permissive trust manager or certificate bypass.
Never use an "accept all certificates" configuration in production.
9. Certificate rotation
A production rotation plan should look like:
Current key
+
Future backup key
↓
Release application
↓
Deploy server certificate using current key
↓
Later rotate to backup key
↓
Release next app version with new backup key
Maintain an emergency plan for replacing pins if a private key is compromised.
10. Testing pinning
Test at least:
Valid certificate
Correct server
→ connection succeeds
Wrong certificate
Intercepted/wrong certificate
→ connection fails
Expired certificate
Expired certificate
→ connection fails
Rotation
Primary certificate
→ succeeds
Backup certificate
→ succeeds
Unknown certificate
→ fails
Test these on actual Android and iOS devices, not only emulators/simulators.
11. Security considerations
Certificate pinning does not protect against every attack.
For example, a compromised application environment can potentially alter or instrument client-side validation. Pinning should therefore be treated as one layer in a broader security model.
Also consider:
- Secure token storage
- Short-lived access tokens
- Backend authorization
- Rate limiting
- Device integrity signals where appropriate
- Secure logging
- Key management
- Server-side monitoring
12. Production checklist
- Define a threat model.
- Decide whether certificate or public-key pinning is appropriate.
- Keep at least one planned backup pin where your architecture supports it.
- Document certificate rotation.
- Test rotation before production.
- Ensure production cannot enable certificate bypass.
- Avoid logging secrets or certificate validation details that expose sensitive information.
- Test both Android and iOS.
- Have an emergency recovery plan.
Conclusion
Certificate pinning is powerful because it narrows the set of server identities your application accepts.
But the real engineering challenge is not adding a pin. It is operating that pin safely for years.
Treat pinning as a lifecycle problem involving security, mobile releases, certificates, and backend operations—not just a networking feature.
Useful Links
Website: www.v-modal.com
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutterter
SDK Android: https://github.com/v-modal/vmodal_sdk_androidoid
Discord: https://discord.gg/K72z28KUx
Top comments (0)