Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Kryptographie & Protokolle Glossar

Certificate Pinning - Zertifikat-Pinning in Apps

Certificate Pinning ist eine Sicherheitstechnik bei der eine Applikation nur bestimmte TLS-Zertifikate oder Public Keys akzeptiert statt dem allgemeinen CA-Vertrauenssystem. Verhindert Man-in-the-Middle-Angriffe selbst wenn Angreifer ein CA-signiertes Zertifikat besitzen. Hauptsächlich in mobilen Apps eingesetzt. Bypass-Methoden: Frida-Hooking, SSL Kill Switch, benutzerdefiniertes Root-Zertifikat. Risiko: Certificate Pinning kann legitimen Traffic-Analyse-Tools blockieren.

Certificate Pinning ist eine Technik um Man-in-the-Middle-Angriffe zu verhindern, die trotz gültigem CA-signiertem Zertifikat ausgeführt werden könnten. Klassische TLS-Validierung vertraut jedem Zertifikat das von einer installierten Root-CA signiert wurde - und es gibt hunderte solcher Root-CAs weltweit. Certificate Pinning reduziert dieses Vertrauen auf ein einzelnes Zertifikat oder einen einzelnen Public Key.

Funktionsprinzip

Warum normales TLS nicht genug ist:

  Normales TLS-Validierung:
  App → Server: "Hier ist mein Zertifikat (signiert von DigiCert)"
  App prüft:    Ist DigiCert eine vertrauenswürdige CA? JA → Verbindung OK!

  Problem: Angreifer hat kompromittierte CA oder eigene CA:
  → Wenn auf Gerät eine MITM-CA installiert wird (z.B. Burp Suite):
    App → Proxy: "Hier ist mein Zertifikat (signiert von PortSwigger CA)"
    App prüft: Ist PortSwigger vertrauenswürdig? WENN JA → MITM erfolgreich!

  Mit Certificate Pinning:
  App hat den echten Server-Public-Key eingespeichert
  App → Proxy: "Hier ist mein Zertifikat..."
  App prüft: Stimmt Public Key mit gespeichertem Pin überein?
  → Proxy-Zertifikat hat anderen Key → VERBINDUNG ABGELEHNT!

Pinning-Varianten:

1. Certificate Pinning:
   → Vollständiges Zertifikat gespeichert
   → Sehr streng: Auch bei Zertifikats-Erneuerung muss neues Zertifikat gepinnt werden!
   → Risiko: zertifikatsablauf → App bricht → Update nötig

2. Public Key Pinning (empfohlen):
   → Nur Public Key der Leaf- oder Intermediate-CA gespeichert
   → Flexibler: Zertifikat kann erneuert werden solange Key gleich bleibt
   → HPKP (HTTP Public Key Pinning): veraltet, im Browser abgeschafft
   → Mobile Apps: noch weit verbreitet

3. CA-Pinning:
   → Nur die spezifische CA gepinnt (nicht das Zertifikat)
   → Flexibel bei Zertifikatserneuerungen
   → Schwächer: alle Zertifikate dieser CA werden akzeptiert

Hash-Format für Pinning:
  # SHA-256 Hash des SubjectPublicKeyInfo:
  openssl s_client -connect example.com:443 2>/dev/null | \
    openssl x509 -pubkey -noout | \
    openssl pkey -pubin -outform DER | \
    openssl dgst -sha256 -binary | \
    base64
  → Ergebnis: "Lkcd0...=" → dieser Hash wird in die App eingespeichert

Implementierung

iOS (App Transport Security):

Info.plist:
  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSPinnedDomains</key>
    <dict>
      <key>api.example.com</key>
      <dict>
        <key>NSIncludesSubdomains</key><false/>
        <key>NSPinnedCAIdentities</key>
        <array>
          <dict>
            <key>SPKI-SHA256-BASE64</key>
            <string>ABC123...</string>
          </dict>
        </array>
      </dict>
    </dict>
  </dict>

iOS (URLSession manuell):
  extension MyDelegate: URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: ...) {
      let serverTrust = challenge.protectionSpace.serverTrust!
      let cert = SecTrustGetCertificateAtIndex(serverTrust, 0)!
      let pubKey = SecCertificateCopyKey(cert)!
      let pubKeyData = SecKeyCopyExternalRepresentation(pubKey, nil)!
      let hash = SHA256.hash(data: pubKeyData as Data)
      let base64 = Data(hash).base64EncodedString()
      if base64 == PINNED_HASH {
        completionHandler(.useCredential, ...)
      } else {
        completionHandler(.cancelAuthenticationChallenge, nil)
      }
    }
  }

Android (Network Security Config):

res/xml/network_security_config.xml:
  <network-security-config>
    <domain-config>
      <domain includeSubdomains="false">api.example.com</domain>
      <pin-set expiration="2027-01-01">
        <pin digest="SHA-256">PRIMARY_PIN_HASH==</pin>
        <pin digest="SHA-256">BACKUP_PIN_HASH==</pin>
      </pin-set>
    </domain-config>
  </network-security-config>

AndroidManifest.xml:
  <application android:networkSecurityConfig="@xml/network_security_config">

OkHttp (Java/Kotlin):
  val client = OkHttpClient.Builder()
    .certificatePinner(
      CertificatePinner.Builder()
        .add("api.example.com", "sha256/PRIMARY_PIN==")
        .add("api.example.com", "sha256/BACKUP_PIN==")
        .build()
    )
    .build()

Pinning Bypass im Pentest

Certificate-Pinning-Bypass-Techniken:

Voraussetzung: Root-Zugriff oder Debugging-Modus auf Gerät

Methode 1 - Frida (Dynamic Instrumentation):
  → Injects JavaScript in laufende App
  → Überschreibt TLS-Validierungsfunktionen zur Laufzeit

  frida-ios-dump / objection:
    objection -g com.example.app explore
    ios sslpinning disable
    → Deaktiviert Pinning in App-Prozess!

  Android:
    objection -g com.example.app explore
    android sslpinning disable

  Universal Frida-Script (ssl-kill-switch2-equivalent):
    → Hook auf TrustManager.checkServerTrusted()
    → Gibt immer true zurück → alle Zertifikate akzeptiert!

Methode 2 - SSL Kill Switch (iOS):
  → Tweak für gejailbreakte iOS-Geräte
  → Deaktiviert Pinning systemweit oder pro App
  → Über Cydia/Sileo installierbar

Methode 3 - Binäre Manipulation:
  → APK/IPA dekompilieren (jadx, apktool)
  → Pinning-Validierungscode analysieren
  → Hash-Vergleich entfernen oder durch eigenen Hash ersetzen
  → App neu signieren + installieren

Methode 4 - Debugging mit Xposed Framework (Android):
  → JustTrustMe Module: überschreibt Java SSL-Klassen
  → TrustMeAlready: ähnlich, mehr Kompatibilität

Methode 5 - Emulator mit benutzerdefinierten Root-CA:
  → Android-Emulator: adb root → Zertifikat in System-Store
  → iOS-Simulator: macOS vertraut simulierten Verbindungen

Erkennungsmaßnahmen gegen Bypass:
  □ Root/Jailbreak-Detection (SafetyNet/Play Integrity, jailbreak-detect)
  □ Debugger-Detection: IsDebuggerPresent, anti-Frida-Checks
  □ Frida-Detection: bekannte Frida-Dateipfade/Ports prüfen
  □ Integritätsprüfung: App-Signatur beim Start verifizieren
  → Aber: Diese Maßnahmen können ebenfalls umgangen werden!
  → Entschlossenem Angreifer kann Pinning nicht standhalten
  → Ziel: Aufwand erhöhen, nicht unmöglich machen

Best Practices

Certificate Pinning korrekt implementieren:

1. Backup-Pins immer einschließen:
   → Mind. 2 Pins: aktueller Key + Backup-Key
   → Backup-Key: nächster Key der bei Rotation verwendet wird
   → Ohne Backup: Rotation → App funktioniert nicht!

2. Expiration Date setzen:
   <pin-set expiration="2027-06-01">
   → Pin-Set läuft ab → App fällt auf normale Validierung zurück
   → Notfall-Fallback wenn Key-Rotation versäumt

3. Reporting aktivieren:
   → Pinning-Fehler an Logging-Endpunkt senden
   → Ermöglicht Erkennung von MITM-Versuchen in Produktion
   → Indikator: viele Pinning-Fehler = MITM-Angriff oder falsche Konfiguration

4. Nicht für alle Verbindungen:
   → Nur für kritische API-Endpunkte (Authentifizierung, Zahlungen)
   → Analytics, CDN, Werbung: kein Pinning (zu viel Rotation)

5. Rotation-Prozess definieren:
   □ Neuen Key generieren
   □ Neuen Hash in App-Code aufnehmen
   □ App-Update deployen (alle User müssen updaten!)
   □ Dann: echtes Zertifikat mit neuem Key deployen
   □ Altes Pin entfernen (nach Übergangszeit)

Wann auf Pinning verzichten:
  □ Wenn kein kontrolliertes Deployment möglich (lange Update-Zyklen)
  □ Wenn Backend von CDN/Cloud-Provider betrieben (häufige Rotation)
  □ Für öffentliche Web-Apps (Browser-Zugriff: kein Pinning möglich)
  → Alternative: Certificate Transparency Monitoring + HSTS Preloading

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung