Certificate Pinning in Flutter: Secure API Communication
Mobile applications frequently communicate with backend APIs. HTTPS protects traffic in transit, but some applications require an additional layer of trust verification.
Certificate pinning allows an application to restrict which certificate or public key it trusts for a particular server.
In this tutorial, we will explore certificate pinning in Flutter, the security model behind it, and a production-friendly implementation approach.
How Certificate Pinning Works
Normally, a client trusts certificates issued by trusted Certificate Authorities.
With pinning, the application additionally checks that the server certificate or public key matches a value the application already trusts.
The flow becomes:
Flutter App
↓
HTTPS Connection
↓
Certificate Validation
↓
Pinned Certificate / Public Key
↓
API Server
When Should You Use Pinning?
Pinning can be valuable for applications handling sensitive traffic, such as:
- Financial applications
- Enterprise systems
- Healthcare platforms
- Internal APIs
- High-value authentication flows
It also introduces operational complexity, especially when certificates are rotated.
Important Certificate Rotation Consideration
A pinned certificate can expire or be replaced. If the application only accepts one exact certificate, a server certificate change can break every installed version of the app.
Plan rotation before implementing pinning.
A safer operational strategy is to maintain a controlled set of valid pins and have a release process for updating them.
Flutter's HTTP Layer
Flutter applications commonly use packages such as http or Dio. Certificate pinning is not simply an HTTP header; it requires control over TLS certificate validation.
For advanced use cases, platform-native networking or a networking stack that explicitly supports certificate validation may be appropriate.
Conceptual Validation
The validation logic is:
Connect to api.example.com
↓
Perform TLS handshake
↓
Read server certificate/public key
↓
Compare against trusted pin
↓
Match? → Continue
No match? → Reject connection
Do not disable certificate validation as a workaround for development errors.
Using a Custom HttpClient
Dart's HttpClient exposes the TLS connection through badCertificateCallback, but this callback must be used carefully.
import 'dart:io';
final client = HttpClient();
client.badCertificateCallback = (
X509Certificate certificate,
String host,
int port,
) {
// Do not blindly return true in production.
return false;
};
Returning true for every certificate effectively disables an important part of TLS validation and is not certificate pinning.
A real pinning implementation should verify the certificate or public-key material against a trusted value.
Dio Integration
If your application uses Dio, isolate the transport layer so certificate-validation logic does not leak into business code.
import 'package:dio/dio.dart';
final dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
),
);
For native TLS controls, use a platform-specific adapter or a maintained package that explicitly supports certificate/public-key pinning for your target platforms.
Testing Pinning
Test at least these scenarios:
- Correct server certificate.
- Expired certificate.
- Untrusted certificate.
- Wrong pinned certificate.
- Certificate rotation.
- No network connection.
The application should fail closed when the TLS identity cannot be trusted.
Common Mistakes
Disabling TLS Validation
Never use:
return true;
for every certificate in production.
Pinning Without a Rotation Plan
Certificate rotation can make old application versions unable to connect.
Treating Pinning as Authentication
Pinning verifies the TLS endpoint identity. It does not replace user authentication, authorization, access tokens, or server-side security controls.
Best Practices
- Use HTTPS everywhere.
- Keep pins out of business logic.
- Plan certificate rotation.
- Test old and new certificates before release.
- Fail closed when validation fails.
- Keep dependencies updated.
- Combine pinning with strong server authentication.
Conclusion
Certificate pinning can provide an additional layer of protection for sensitive Flutter applications, but it should be implemented carefully. The biggest engineering challenge is not adding a certificate check; it is designing a reliable certificate lifecycle that continues working when certificates change.
For production applications, use a maintained networking solution with explicit certificate/public-key pinning support rather than implementing TLS validation from scratch.
Stay tuned for more advanced Flutter security tutorials!
Useful Links
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)