Implementing Certificate Pinning¶
Certificate pinning is a critical security measure that ensures your Flutter app communicates only with trusted servers by validating their SSL/TLS certificates against a predefined set of trusted certificates. This prevents man-in-the-middle (MITM) attacks and ensures data integrity during network requests.
Why Certificate Pinning Matters¶
Without certificate pinning, your app is vulnerable to: - MITM attacks where attackers intercept traffic between the app and the server. - Certificate spoofing where a malicious server mimics a legitimate one. - Manually configured trust stores that may include compromised certificates.
Pinning mitigates these risks by enforcing strict certificate validation.
Implementation Steps¶
1. Prepare Trusted Certificates¶
- Extract the server's SSL certificate (e.g.,
example.com.crt) using tools likeopenssl: - Save the certificate to a file (e.g.,
assets/cert.pem).
2. Configure HTTP Client with Pinned Certificate¶
Use the http_certificate_pinning package to enforce certificate pinning. This library provides a streamlined API for pinning certificates with the http package.
Example: Using http_certificate_pinning for Certificate Pinning¶
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
import 'package:http/http.dart' as http;
void main() async {
// Load the trusted certificate from assets
final certificate = await X509Certificate.fromPemFile('assets/cert.pem');
// Create a pinned HTTP client
final client = http.Client(
pinCertificates: [certificate],
);
// Make a secure request
final response = await client.get(Uri.parse('https://example.com'));
print(await response.body);
}
3. Validate Server Certificates¶
Ensure the server's certificate matches the pinned one. For example, verify the subject and issuer, and check for expiration:
Example: Certificate Validation with Expiration Check¶
final certificate = await X509Certificate.fromPemFile('assets/cert.pem');
final serverCert = await request.connection!.serverCertificate;
if (serverCert != certificate) {
throw Exception('Certificate mismatch');
}
// Check if certificate is expired
if (serverCert.notAfter.isBefore(DateTime.now())) {
throw Exception('Certificate expired');
}
4. Handle Edge Cases¶
- Expired Certificates: Reject connections if the certificate is expired (see validation example above).
- Multiple Certificates: Pin all trusted certificates to avoid partial trust.
- Dynamic Trust Stores: Update the trust store securely (e.g., via secure storage or OTA updates).
Diagram: Certificate Pinning Workflow¶
Key Takeaways¶
- Certificate pinning prevents MITM attacks by validating server certificates.
- Use the
http_certificate_pinningpackage for a streamlined pinning workflow. - Always validate the server's certificate against the pinned one during TLS handshakes.
- Securely update the trust store to handle certificate rotations or new trusted servers.