İçeriğe geç
Muhammed Ali
Kocabey

Android Sertifika Sabitlemesini Doğru Yapmak

Android'de sertifika sabitlemesini doğru yapmak, sunucunun açık anahtarının SubjectPublicKeyInfo (SPKI) hash'ini sabitlemek, en az bir yedek pin dağıtmak ve ihtiyaç doğmadan anahtar rotasyonunu planlamak demektir. Bunun iki yolu vardır: platformun uyguladığı XML tabanlı Network Security Config veya OkHttp'nin CertificatePinner sınıfı. API kullanımı kolaydır; zor olan, sıradan bir sertifika yenilemesinin kurulu uygulamaların bağlantısını kesmesini önleyen operasyonel disiplindir.

Android'de eID çipi okuma yazısında anlattığım NFC/eID akışlarını da içeren bir bankacılık uygulamasında pinning kullandım. Çıkardığım ders, pinning'in yanlış yönetildiğinde kendi erişilebilirliğimizi tehdit etmesiydi. Bu yazı o riski nasıl yönettiğimizle ilgilidir.

Temel noktalar

  • Sertifikayı değil, SPKI açık anahtarını sabitle. Sertifika her yenilemede değişir; anahtar çifti aynı kalabilir. SPKI pin'i, aynı anahtarla yenilenen sertifikada çalışır.
  • İki mekanizma: platform HTTPS trafiği için Network Security Config; OkHttpClient başına programatik CertificatePinner. İkisini farklı pin kümeleriyle yarım yamalak yapılandırmayın.
  • Yedek pin dağıt. Anahtar veya CA değiştiğinde ikinci pin yoksa ancak uygulama güncellemesiyle bağlantıyı geri getirebilirsiniz; güncellemeyen kullanıcılar dışarıda kalır.
  • Rotasyonu yayınlamadan planla. İlk sürümde aktif anahtar ve çevrimdışı tutulan yedek anahtarın pin'i bulunsun. Yedeğe geçince yeni yedek üretin.
  • Debug proxy'sini release pin'leriyle engelleme. Yerel Proxyman/Charles kullanımı için debug-only trust anchor ayarlayın.

Neden pinning, neden Google temkinli?

Normal Android TLS, sunucu sertifikasını sistemin güven deposuna göre doğrular. Pinning ek koşul getirir: zincirde önceden seçtiğiniz anahtar da bulunmalıdır. Bu, cihazın güvendiği bir CA'dan geçerli sertifika elde etmiş saldırganın riskini azaltır; ele geçirilmiş veya kötüye kullanılan CA ile saldırganın eklediği root sertifika bu tehdit modelinin örnekleridir.

Bedeli erişilebilirliktir. Google'ın Network Security Config rehberi bu nedenle temkinlidir. Pin uyumsuzluğu esnek bir geri dönüş sunmaz; bağlantı başarısız olur. Yanlış pin veya eksik yedek, uygulamanın backend ile konuşmasını engelleyebilir. Pinning her uygulamada varsayılan olarak açılacak bir kontrol değil, MITM riskini azaltmak için bilinçli alınan bir işletim riskidir. SecureCheck sinyalleri gibi bunun da hata biçimini sahiplenmek gerekir.

Birinci yöntem: Network Security Config

Platformun sunduğu yöntem, manifest'ten işaret edilen XML dosyasıdır. HttpsURLConnection, platform trust manager'ını kullanan OkHttp ve WebView gibi platform doğrulamasından geçen bağlantıları kapsar. Pin'ler <domain-config> içindeki <pin-set> altında yer alır:

<!-- res/xml/network_security_config.xml -->
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">api.example.com</domain>
        <pin-set expiration="2027-10-01">
            <!-- Base64 SHA-256 of the SPKI. Primary key in use today. -->
            <pin digest="SHA-256">k3hBDZ8m0cJ8yq1Yd0pPtQx2cHjWn7p9vF4sL1aXuGk=</pin>
            <!-- Backup key, held offline, not yet serving traffic. -->
            <pin digest="SHA-256">YwFp2nL8qR6tZ3vK1mD9xS0cJ7hG4bN5aU8eW2oP1rM=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

Manifest bağlantısı bir kez yapılır:

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

digest="SHA-256" desteklenen hash algoritmasıdır. Değer tüm sertifikanın değil, SPKI'nin hash'inin base64 karşılığıdır. expiration bir emniyet çıkışıdır: belirtilen tarihten sonra framework pinning uygulamayı bırakıp normal CA doğrulamasına döner. Bu, eski uygulamanın kilitlenmesini önler ama güvenlik bedeli vardır. O tarihten sonra kötü niyetli CA sertifikasına karşı ek koruma kalkar. Tarihi izleyin ve sürümlerle birlikte bilinçli olarak güncelleyin. Örnek pin değerlerini kendi sunucunuzun gerçek değerleri yerine kullanmayın.

İkinci yöntem: OkHttp CertificatePinner

Trafik OkHttp üzerinden gidiyorsa ve kodda tanımlanan, test edilebilen pinning istiyorsanız CertificatePinner kullanın. Aynı base64 SHA-256 SPKI değerleri sha256/ önekiyle yazılır:

val certificatePinner = CertificatePinner.Builder()
    .add(
        "api.example.com",
        "sha256/k3hBDZ8m0cJ8yq1Yd0pPtQx2cHjWn7p9vF4sL1aXuGk=", // primary
        "sha256/YwFp2nL8qR6tZ3vK1mD9xS0cJ7hG4bN5aU8eW2oP1rM=", // backup
    )
    .build()

val client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

Pinning hostname bazındadır; api.example.com ile www.api.example.com ayrı girdilerdir. Sabitlenen client'ın tek sahibi olması ve diğer modüllerin fark ettirmeden pinsiz client oluşturmaması için bu yapı :core:network gibi bir modülde tutulabilir. Android modül sınırları yazısında benzer sorumluluk sınırlarını anlattım. Bir mekanizma seçin; XML ve CertificatePinner farklı pin kümeleri kullanırsa bir kontrol geçip diğeri başarısız olabilir.

Leaf sertifikayı değil, SPKI'yi sabitle

Leaf sertifika her yenilemede yeniden düzenlenir; otomatik CA'larda bu genellikle kısa aralıklarla olur, iptal durumunda da yenisi gerekir. Sertifikanın tamamının hash'ini sabitlerseniz her yenileme uygulama güncellemesi gerektiren değişiklik olur. SPKI sabitlenirse CSR aynı anahtar çiftini kullandığı sürece sertifika değişse de pin eşleşir.

Canlı sunucudan SPKI hash'i şöyle hesaplanabilir:

openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout \
  | openssl pkey -pubin -outform der \
  | openssl dgst -sha256 -binary \
  | openssl enc -base64

İkinci pin'i elde etmek için aynı işlemi yedek anahtarın sertifikasına veya CSR'sindeki açık anahtara uygulayın. OkHttp'de kontrollü bir testte yanlış pin kullanıp exception içindeki doğru hash değerlerini de inceleyebilirsiniz; bunu yalnız güvenilen ağda yapın, debugging proxy'sinin anahtarını yanlışlıkla sabitlemeyin.

Her zaman yedek pin yapılandır

Tek pin, anahtar değişimini bağlantı kesintisine dönüştürür. Anahtarın ele geçirilmesi, CA geçişi veya HSM değişimi sonrası eşleşen başka anahtar yoksa acil uygulama sürümü gerekir ve bütün kullanıcıların güncellemesi beklenir.

Yedek pin bu çıkmazı önler: bugün üretilen ve çevrimdışı tutulan ikinci anahtarın hash'i aktif anahtarla birlikte dağıtılır. Uygulama onu zaten tanıdığı için sunucu bu anahtara geçirilebilir. Üretimde en az iki pin ve kontrol ettiğiniz anahtarlarla çalışın.

Uygulamayı kilitlemeyen rotasyon

Yedek anahtar kullanıldığında yeni yedekle yerine konmalıdır. Dönen pencere şöyledir:

  1. İki pin yayınla: trafik sunan A, çevrimdışı yedek B.
  2. Rotasyonda sunucuyu B'ye geçir. Uygulama B'yi zaten sabitler; bu geçiş için yeni release gerekmez.
  3. Sonraki uygulama sürümünde C üret, pin'ini ekle ve emekliye ayrılan A'yı kaldır. Aktif ve yedek iki anahtarlı düzene dönülür.

Bir uygulama sürümünün pin kümesinde, sonraki sürüm kullanıcıların çoğuna ulaşana kadar geçerli kalacak anahtar bulunmalıdır. Pin expiration ve yayın sıklığını buna göre planlayın. Sertifika ve anahtar sürelerini diğer üretim bağımlılıkları gibi izleyin; rotasyonu kendi takviminizle yönetin.

Sık yapılan hatalar

  • Leaf sertifikayı sabitlemek: her yenilemede kırılır; SPKI kullanın.
  • Yedek pin olmaması: anahtar değişimini zorunlu güncelleme kesintisine çevirir.
  • Rotasyon planının olmaması: iki pin de yenilenmiyorsa yalnız kesintiyi geciktirir. Dönen pencereyi süreçleştirin.
  • Debug build'de release pin'lerini uygulamak: geliştiricinin Proxyman/Charles ile kendi trafiğini incelemesini engeller. Yalnız debuggable build'de etkin trust anchor tanımlayın:
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
    <!-- pin-set as above applies to release -->
</network-security-config>
  • Geçmişte kalmış expiration: uygulama çalışmayı sürdürürken pinning sessizce kapanır. Tarihi ileri taşımayı yayın ve izleme sürecinin parçası yapın.

Doğru yönetilen pinning, kontrol edilen aktif anahtar, yanındaki yedek ve rotasyon takviminden oluşur. API birkaç dakikalık iş olabilir; korumayı güvenilir kılan, bütün operasyonel plandır.

BİRLİKTE ÇALIŞALIM

İyi bir problemle başlayalım.

Ekibiniz için bir Android mühendisi arıyorsanız veya bir teknik problemi birlikte değerlendirmek istiyorsanız konuşalım.

30 dakika görüşelim[email protected]Tüm iletişim ve podcast bağlantıları