DEV Community

vmodal_ai
vmodal_ai

Posted on

Certificate Pinning in Flutter

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
Enter fullscreen mode Exit fullscreen mode

With pinning:

Flutter App
   ↓
TLS validation
   ↓
Platform trust store
   +
Pinned certificate/public key
   ↓
Server
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

When certificate A expires:

Server → certificate B
App → rejects B
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Wrong certificate

Intercepted/wrong certificate
→ connection fails
Enter fullscreen mode Exit fullscreen mode

Expired certificate

Expired certificate
→ connection fails
Enter fullscreen mode Exit fullscreen mode

Rotation

Primary certificate
→ succeeds

Backup certificate
→ succeeds

Unknown certificate
→ fails
Enter fullscreen mode Exit fullscreen mode

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)