Skip to content

Pinning Exceptions

Handling Pinning Exceptions

Certificate pinning is a critical security measure, but it introduces risks when exceptions occur—such as certificate mismatches, expired certificates, or revoked trust chains. Properly handling these scenarios ensures user trust, prevents crashes, and maintains security.


Detecting Pinning Exceptions

Pinning exceptions are typically triggered during TLS handshakes. In Flutter, frameworks like http or dio provide mechanisms to intercept and handle these errors.

Example: Interceptor for SSL Errors

import 'package:dio/dio.dart';
import 'package:dio_interceptors/dio_interceptors.dart';

class SSLExceptionInterceptor extends Interceptor {
  @override
  void onError(DioException error, ErrorInterceptorHandler handler) {
    if (error.type == DioExceptionType.receiveTimeout || 
        error.type == DioExceptionType.sendTimeout) {
      // Handle network timeouts
    } else if (error.type == DioExceptionType.response) {
      // Handle HTTP errors
    } else if (error.type == DioExceptionType.other) {
      // Check for certificate mismatch or expiration
      final sslError = error.error as SSLException;
      if (sslError != null && sslError.cert != null) {
        final certificate = sslError.cert;
        final now = DateTime.now();
        if (certificate.notBefore.isAfter(now) || certificate.notAfter.isBefore(now)) {
          // Certificate is expired or not yet valid
          await showNotification('Certificate Expired: ${certificate.subject}');
          print('Certificate expired: ${certificate.subject}');
        } else {
          // Check SAN for mismatch
          if (!certificate.subjectAltNames.contains('expected.san.example.com')) {
            await showNotification('Certificate Mismatch: ${certificate.subject}');
            print('Certificate mismatch: ${certificate.subject}');
          }
        }
      }
    } else if (error.type == DioExceptionType.cancel) {
      // Handle request cancellation
    } else {
      // Fallback for unknown errors
      print('Unknown error: ${error.message}');
    }
    handler.next(error);
  }
}

Diagram: SSL Exception Detection Flow

[Request Sent]  
       |  
       v  
[SSL Handshake]  
       |  
       v  
[Exception Detected]  
       |  
       v  
[Intercept and Handle Error]  
       |  
       v  
[User Notified or Fallback Triggered]  

Handling Expired Certificates

Expired certificates indicate a potential security risk. Apps should:
1. Validate certificate validity using the NotBefore and NotAfter fields.
2. Log the event for monitoring.
3. Notify users to check their connection or update the app.

Example: Checking Certificate Expiry

import 'dart:security';

Future<void> checkCertificateValidity(X509Certificate certificate) async {
  final now = DateTime.now();
  if (certificate.notBefore.isAfter(now) || certificate.notAfter.isBefore(now)) {
    // Certificate is expired or not yet valid
    await showNotification('Certificate Expired: $certificate.subject');
    // Log the event for monitoring
    print('Certificate expired: ${certificate.subject}');
  }
}

User Notification and Fallback Mechanisms

When pinning fails, apps should:
- Avoid automatic retries to prevent infinite loops.
- Notify users via in-app dialogs or logs.
- Provide fallback options (e.g., redirecting to a trusted endpoint).

Example: User Notification Dialog

import 'package:flutter/material.dart';

void showNotification(String message) {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('SSL Error'),
      content: Text(message),
      actions: [
        TextButton(
          onPressed: Navigator.of(context).pop,
          child: Text('OK'),
        ),
      ],
    ),
  );
}

Graceful Degradation Strategies

In rare cases, apps may need to:
- Retry with a different certificate (e.g., a fallback CA bundle).
- Redirect to a secure endpoint with a known valid certificate.
- Disable pinning temporarily for debugging (but only in development).

Note: These strategies should be used sparingly and with strict validation to avoid compromising security.


Security Considerations

  • Avoid automatic overrides for pinning exceptions; this can expose the app to man-in-the-middle attacks.
  • Implement HSTS headers to enforce HTTPS and prevent downgrade attacks.
  • Monitor certificate validity regularly and update pinned certificates as needed.

Key takeaways

  • Detect SSL errors using interceptors to handle pinning exceptions gracefully.
  • Validate certificate expiry and notify users to maintain trust.
  • Avoid automatic retries and prioritize user notification over fallbacks.
  • Balance security and usability by avoiding temporary overrides in production.
  • Monitor and update pinned certificates to ensure long-term security.

Note: The examples above are specific to Flutter/Dio. For cross-platform implementations, consider using standard Dart libraries like dart:io or other HTTP clients that provide similar SSL error handling capabilities.