İstemci-sunucu şifrelenmiş etkileşimleri, uygulamanızın verilerini korumak için Taşıma Katmanı Güvenliği (TLS) kullanır.
Bu makalede, güvenli ağ protokolü en iyi uygulamaları ve Herkese Açık Anahtar Altyapısı (PKI) ile ilgili dikkat edilmesi gereken noktalar ele alınmaktadır. Daha fazla bilgi için Android Güvenliğine Genel Bakış ve İzinlere Genel Bakış bölümünü okuyun.
Kavramlar
TLS sertifikası olan bir sunucunun ortak anahtarı ve eşleşen özel anahtarı vardır. Sunucu, TLS el sıkışma sırasında sertifikasını imzalamak için ortak anahtar kriptografisini kullanır.
Basit bir el sıkışma işlemi yalnızca sunucunun sertifikanın özel anahtarını bildiğini kanıtlar. Bu durumu düzeltmek için istemcinin birden fazla sertifikaya güvenmesine izin verin. Sertifikası istemci tarafında güvenilir sertifika grubunda görünmeyen sunuculara güvenilmez.
Ancak sunucular, sertifikalarının ortak anahtarını yeni bir anahtarla değiştirmek için anahtar döndürme işlemini kullanabilir. Sunucu yapılandırması değişikliği, istemci uygulamasını güncellemeyi gerektirir. Sunucu bir web tarayıcısı veya e-posta uygulaması gibi üçüncü taraf web hizmetiyse istemci uygulamasını ne zaman güncelleyeceğinizi belirlemek daha zordur.
Sunucular genellikle sertifika yayınlamak için Sertifika Yetkilileri (CA) sertifikalarını kullanır. Bu sayede istemci tarafı yapılandırması zaman içinde daha kararlı olur. CA, özel anahtarını kullanarak sunucu sertifikasını imzalar. İstemci daha sonra sunucunun platformda bilinen bir CA sertifikasına sahip olup olmadığını kontrol edebilir.
Güvenilir CA'lar genellikle barındırma platformunda listelenir. Android 8.0 (API düzeyi 26), her sürümde güncellenen ve cihazlar arasında değişmeyen 100'den fazla CA içerir.
CA, çok sayıda sunucu için sertifika sunduğundan istemci uygulamalarının sunucuyu doğrulayacak bir mekanizmaya ihtiyacı vardır. CA'nın sertifikası, sunucuya gmail.com gibi belirli bir ad veya *.google.com gibi bir joker karakter kullanarak tanımlar.
Bir web sitesinin sunucu sertifikası bilgilerini görüntülemek için openssl
aracının s_client
komutunu kullanarak bağlantı noktası numarasını girin. HTTPS varsayılan olarak 443 numaralı bağlantı noktasını kullanır.
Komut, openssl s_client
çıkışını openssl x509
'e iletir. openssl x509
, sertifika bilgilerini X.509 standardında biçimlendirir. Komut, konuyu (sunucu adı) ve vereni (CA) ister.
openssl s_client -connect WEBSITE-URL:443 | \ openssl x509 -noout -subject -issuer
HTTPS örneği
Tanınmış bir CA tarafından verilen sertifikaya sahip bir web sunucunuz olduğunu varsayarak aşağıdaki kodda gösterildiği gibi güvenli bir istek gönderebilirsiniz:
Kotlin
val url = URL("https://wikipedia.org") val urlConnection: URLConnection = url.openConnection() val inputStream: InputStream = urlConnection.getInputStream() copyInputStreamToOutputStream(inputStream, System.out)
Java
URL url = new URL("https://wikipedia.org"); URLConnection urlConnection = url.openConnection(); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
HTTP isteklerini özelleştirmek için HttpURLConnection
'e yayınlayın.
Android HttpURLConnection
dokümanlarında istek ve yanıt başlıklarını işleme, içerik yayınlama, çerezleri yönetme, proxy kullanma, yanıtları önbelleğe alma ve daha fazlasıyla ilgili örnekler yer alır. Android çerçevesi, bu API'leri kullanarak sertifikaları ve ana makine adlarını doğrular.
Mümkün olduğunda bu API'leri kullanın. Aşağıdaki bölümde, farklı çözümler gerektiren yaygın sorunlar ele alınmaktadır.
Sunucu sertifikalarını doğrulamayla ilgili yaygın sorunlar
getInputStream()
işlevinin içerik döndürmek yerine istisna attığını varsayalım:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374) at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433) at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290) at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240) at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282) at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177) at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
Bu durum aşağıdakiler dahil olmak üzere çeşitli nedenlerden kaynaklanabilir:
- Sunucu sertifikasını veren CA bilinmiyordu.
- Sunucu sertifikası bir CA tarafından değil, kendinden imzalanmıştır.
- Sunucu yapılandırmasında ara CA eksik.
Aşağıdaki bölümlerde, sunucuyla bağlantınızın güvenliğini sağlarken bu sorunların nasıl ele alınacağı açıklanmaktadır.
Bilinmeyen sertifika yetkilisi
SSLHandshakeException
hatası, sistemin CA'ya güvenmemesi nedeniyle ortaya çıkar. Bunun nedeni, Android'in güvenmediği yeni bir CA'dan sertifika almanız veya uygulamanızın CA olmadan daha eski bir sürümde çalışması olabilir. CA'lar gizli olduğu için nadiren bilinir. CA'lar genellikle herkese açık CA değil, devlet, şirket veya eğitim kurumu gibi bir kuruluş tarafından kendi kullanımı için verilen özel CA olduğundan bilinmez.
Uygulamanızın kodunu değiştirmek zorunda kalmadan özel CA'lara güvenmek için Ağ Güvenliği Yapılandırması'nı değiştirin.
Dikkat:
Birçok web sitesi, hiçbir şey yapmayan bir TrustManager
yükleme gibi kötü bir alternatif çözümü açıklar.
Bu işlem, kullanıcılarınızın herkese açık bir kablosuz hotspot kullanırken saldırılara açık hale gelmesine neden olur. Saldırganlar, kullanıcılarınızın trafiğini sunucunuz gibi davranan bir proxy üzerinden göndermek için DNS hilelerini kullanabilir. Saldırgan daha sonra şifreleri ve diğer kişisel verileri kaydedebilir. Bu, saldırganın sertifika oluşturabilmesi nedeniyle işe yarar. Sertifikanın güvenilir bir kaynaktan geldiğini doğrulayan bir TrustManager
olmadan bu tür saldırıları engelleyemezsiniz. Bu nedenle, geçici olarak bile olsa bunu yapmayın. Bunun yerine, uygulamanızın sunucu sertifikasının verenine güvenmesini sağlayın.
Kendinden imzalı sunucu sertifikası
İkinci olarak, SSLHandshakeException
kendinden imzalı bir sertifika nedeniyle oluşabilir. Bu durumda sunucu kendi CA'sı olur. Bu, bilinmeyen bir sertifika yetkilisine benzer. Bu nedenle, uygulamanızın Ağ Güvenliği Yapılandırması'nı, kendi imzaladığınız sertifikalara güvenecek şekilde değiştirin.
Ara sertifika yetkilisi eksik
Üçüncü olarak, SSLHandshakeException
ara CA'nın eksik olması nedeniyle ortaya çıkar. Herkese açık CA'lar, sunucu sertifikalarını nadiren imzalar. Bunun yerine, kök CA ara CA'ları imzalar.
CA'lar, güvenliği ihlal etme riskini azaltmak için kök CA'yı çevrimdışı tutar. Ancak Android gibi işletim sistemleri genellikle doğrudan yalnızca kök CA'lara güvenir. Bu da ara CA tarafından imzalanan sunucu sertifikası ile kök CA'yı tanıyan sertifika doğrulayıcı arasında kısa bir güven boşluğu bırakır.
Sunucu, bu güven açığını ortadan kaldırmak için TLS el sıkışma sırasında sunucu CA'sından ara CA'lar aracılığıyla güvenilir bir kök CA'ya bir sertifika zinciri gönderir.
Örneğin, openssl
s_client
komutu tarafından görüntülenen mail.google.com sertifika zinciri aşağıda verilmiştir:
$ openssl s_client -connect mail.google.com:443 --- Certificate chain 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=mail.google.com i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority ---
Bu, sunucunun mail.google.com için ara CA olan Thawte SGC CA tarafından verilen bir sertifika ve Thawte SGC CA için Android'in güvendiği birincil CA olan Verisign CA tarafından verilen ikinci bir sertifika gönderdiğini gösterir.
Ancak bir sunucu, gerekli ara CA'yı içerecek şekilde yapılandırılmamış olabilir. Örneğin, Android tarayıcılarında hataya ve Android uygulamalarında istisnalara neden olabilecek bir sunucu aşağıda verilmiştir:
$ openssl s_client -connect egov.uscis.gov:443 --- Certificate chain 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3 ---
Bilinmeyen bir CA veya kendinden imzalı sunucu sertifikasının aksine, çoğu masaüstü tarayıcısı bu sunucuyla iletişim kurarken hata oluşturmaz. Masaüstü tarayıcılar, güvenilir ara CA'ları önbelleğe alır. Bir tarayıcı, bir siteden orta CA hakkında bilgi edindikten sonra bu CA'nın sertifika zincirinde tekrar kullanılmasına gerek kalmaz.
Bazı siteler, kaynak sunan ikincil web sunucuları için bunu kasıtlı olarak yapar. Bant genişliğinden tasarruf etmek için ana HTML sayfalarını tam sertifika zinciri olan bir sunucudan, resimlerini, CSS'lerini ve JavaScript'lerini ise CA olmadan yayınlayabilirler. Ne yazık ki bu sunucular bazen Android uygulamanızdan erişmeye çalıştığınız bir web hizmetini sağlıyor olabilir ve bu hizmet o kadar hoşgörülü olmayabilir.
Bu sorunu düzeltmek için sunucuyu, ara CA'yı sunucu zincirine dahil edecek şekilde yapılandırın. Çoğu CA, yaygın web sunucuları için bu işlemin nasıl yapılacağıyla ilgili talimatlar sağlar.
SSLSocket'i doğrudan kullanmayla ilgili uyarılar
Şu ana kadarki örneklerde HttpsURLConnection
kullanılarak HTTPS'ye odaklanıldı.
Bazen uygulamaların HTTPS'den ayrı olarak TLS kullanması gerekir. Örneğin, bir e-posta uygulaması SMTP, POP3 veya IMAP'nin TLS varyantlarını kullanabilir. Bu durumlarda uygulama, HttpsURLConnection
'ın dahili olarak yaptığı gibi SSLSocket
'ü doğrudan kullanabilir.
Sertifika doğrulama sorunlarıyla ilgili olarak şimdiye kadar açıklanan teknikler SSLSocket
için de geçerlidir.
Aslında, özel bir TrustManager
kullanıldığında HttpsURLConnection
'a bir SSLSocketFactory
iletilir.
Bu nedenle, SSLSocket
ile özel bir TrustManager
kullanmanız gerekiyorsa aynı adımları uygulayın ve SSLSocket
'ınızı oluşturmak için bu SSLSocketFactory
'yi kullanın.
Dikkat:
SSLSocket
, ana makine adı doğrulaması yapmaz. Uygulamanızın, tercihen beklenen ana makine adını belirterek getDefaultHostnameVerifier()
çağrısı yaparak kendi ana makine adı doğrulamasını yapması gerekir.
Ayrıca, HostnameVerifier.verify()
hata oluştuğunda istisna atmadığını unutmayın. Bunun yerine, açıkça kontrol etmeniz gereken bir boole sonucu döndürür.
Sertifika doğrulama
TLS, yalnızca sunucu ve alanlarının doğrulanmış sahiplerine sertifika vermek için CA'lardan yararlanır. Nadir durumlarda, CA'lar kandırılır veya Comodo ya da DigiNotar'da olduğu gibi güvenlik ihlali yaşanır. Bu da bir ana makine adının sertifikalarının, sunucu veya alanın sahibinden başka birine verilmesine neden olur.
Android, bu riski azaltmak için sertifika iptallerini sistem genelinde, engellenenler listesi ve sertifika şeffaflığı kombinasyonuyla, online sertifika doğrulamasına gerek kalmadan yönetir. Ayrıca Android, TLS el sıkışmasına ek olarak gönderilen OCSP yanıtlarını da doğrular.
Uygulamanızda Sertifika Şeffaflığı'nı etkinleştirmek için Ağ Güvenlik Yapılandırması dokümanlarımızdaki Sertifika Şeffaflığı'nı etkinleştirme bölümüne bakın.
Uygulamanızı belirli sertifikalarla kısıtlama
Dikkat: Uygulamanız için geçerli kabul edilen sertifikaları daha önce yetkilendirdiğiniz sertifikalarla kısıtlama işlemi olan sertifika sabitleme, Android uygulamaları için önerilmez. Başka bir CA'ya geçmek gibi gelecekteki sunucu yapılandırması değişiklikleri, sabitlenmiş sertifikaları olan uygulamaların istemci yazılımı güncellemesi almadan sunucuya bağlanamamasına neden olur.
Uygulamanızı yalnızca belirttiğiniz sertifikaları kabul edecek şekilde kısıtlamak istiyorsanız tamamen sizin kontrolünüzde olan en az bir anahtar da dahil olmak üzere birden fazla yedek PIN eklemeniz ve uyumluluk sorunlarını önlemek için yeterince kısa bir geçerlilik süresi belirlemeniz önemlidir. Ağ Güvenliği Yapılandırması, bu özelliklerle sabitleme sağlar.
İstemci sertifikaları
Bu makalede, sunucularla iletişimin güvenliğini sağlamak için TLS'nin kullanımına odaklanılmaktadır. TLS, sunucunun istemcinin kimliğini doğrulamasına olanak tanıyan istemci sertifikası kavramını da destekler. Bu makalenin kapsamı dışında olsa da kullanılan teknikler, özel bir TrustManager
belirtmeye benzer.
Nogotofail: Ağ trafiği güvenlik testi aracı
Nogotofail, uygulamalarınızın bilinen TLS/SSL güvenlik açıklarına ve yanlış yapılandırmalara karşı güvenli olduğunu kolayca doğrulamanızı sağlayan bir araçtır. Ağ trafiğinin üzerinden geçebileceği herhangi bir cihazda ağ güvenliği sorunlarını test etmek için kullanılan otomatik, güçlü ve ölçeklenebilir bir araçtır.
Nogotofail, üç temel kullanım alanında faydalıdır:
- Hataları ve güvenlik açıklarını bulma
- Düzeltmeleri doğrulama ve gerileme olup olmadığını izleme.
- Hangi uygulamaların ve cihazların ne kadar trafik oluşturduğunu anlama
Nogotofail; Android, iOS, Linux, Windows, ChromeOS, macOS ve internete bağlanmak için kullandığınız tüm cihazlarda çalışır. Android ve Linux'ta ayarları yapılandırmak ve bildirim almak için bir istemci kullanılabilir. Saldırı motorunun kendisi ise yönlendirici, VPN sunucusu veya proxy olarak dağıtılabilir.
Araca Nogotofail açık kaynak projesi üzerinden erişebilirsiniz.
SSL ve TLS'de yapılan güncellemeler
Android 10
Google Chrome gibi bazı tarayıcılar, bir TLS sunucusu TLS el sıkışmasının bir parçası olarak sertifika isteği mesajı gönderdiğinde kullanıcıların sertifika seçmesine olanak tanır. Android 10'dan itibaren KeyChain nesneleri, kullanıcılara sertifika seçim istemi göstermek için KeyChain.choosePrivateKeyAlias()
çağrısı yaparken sertifika verenleri ve anahtar spesifikasyonu parametrelerini dikkate alır. Özellikle bu istem, sunucu özelliklerine uymayan seçenekler içermez.
Kullanıcı tarafından seçilebilecek sertifika yoksa (ör. sunucu spesifikasyonuyla eşleşen sertifika yoksa veya cihazda yüklü sertifika yoksa) sertifika seçimi istemi hiç gösterilmez.
Ayrıca, Android 10 veya sonraki sürümlerde anahtarları ya da CA sertifikalarını bir KeyChain nesnesine aktarmak için cihaz ekran kilidinin olması gerekmez.
TLS 1.3 varsayılan olarak etkindir
Android 10 ve sonraki sürümlerde TLS 1.3, tüm TLS bağlantıları için varsayılan olarak etkindir. TLS 1.3 uygulamamızla ilgili birkaç önemli ayrıntı aşağıda verilmiştir:
- TLS 1.3 şifre paketleri özelleştirilemez. Desteklenen TLS 1.3 şifre paketleri, TLS 1.3 etkinleştirildiğinde her zaman etkinleştirilir.
setEnabledCipherSuites()
çağrısı yapılarak devre dışı bırakma girişimleri yoksayılır. - TLS 1.3 için pazarlık yapıldığında, oturumlar oturum önbelleği eklenmeden önce
HandshakeCompletedListener
nesneleri çağrılır. (TLS 1.2 ve önceki sürümlerde bu nesneler, oturumlar oturum önbelleği eklendikten sonra çağrılır.) - SSLEngine örneklerinin Android'in önceki sürümlerinde
SSLHandshakeException
atadığı bazı durumlarda, bu örnekler Android 10 ve sonraki sürümlerde bunun yerineSSLProtocolException
atar. - 0-RTT modu desteklenmez.
Dilerseniz SSLContext.getInstance("TLSv1.2")
çağrısını yaparak TLS 1.3'ün devre dışı bırakıldığı bir SSLContext elde edebilirsiniz. Ayrıca, uygun bir nesnede setEnabledProtocols()
çağrısı yaparak protokol sürümlerini bağlantı bazında etkinleştirebilir veya devre dışı bırakabilirsiniz.
SHA-1 ile imzalanan sertifikalara TLS'de güvenilmez
Android 10'da, SHA-1 karma algoritmasını kullanan sertifikalara TLS bağlantılarında güvenilmez. Kök CA'lar 2016'dan beri bu tür sertifikalar vermiyor ve Chrome ya da diğer büyük tarayıcılarda artık bu sertifikalar güvenilir değil.
SHA-1 kullanan bir sertifika sunan bir siteye bağlanmaya çalışıldığında bağlantı girişimleri başarısız olur.
KeyChain davranışı değişiklikleri ve iyileştirmeleri
Google Chrome gibi bazı tarayıcılar, bir TLS sunucusu TLS el sıkışması kapsamında sertifika isteği mesajı gönderdiğinde kullanıcıların sertifika seçmesine olanak tanır. Android 10'dan itibaren, KeyChain
nesneleri, kullanıcılara sertifika seçim istemi göstermek için KeyChain.choosePrivateKeyAlias()
çağrılırken sertifika verenleri ve anahtar spesifikasyonu parametrelerini dikkate alır. Özellikle bu istem, sunucu özelliklerine uymayan seçenekler içermez.
Kullanıcı tarafından seçilebilir sertifika yoksa (ör. sunucu spesifikasyonuyla eşleşen sertifika yoksa veya cihazda yüklü sertifika yoksa) sertifika seçim istemi hiç gösterilmez.
Ayrıca, Android 10 veya sonraki sürümlerde anahtarları ya da CA sertifikalarını bir KeyChain nesnesine aktarmak için cihaz ekran kilidinin olması gerekmez.
Diğer TLS ve kriptografi değişiklikleri
Android 10'da geçerli olacak şekilde TLS ve kriptografi kitaplıklarında birkaç küçük değişiklik yapıldı:
- AES/GCM/NoPadding ve ChaCha20/Poly1305/NoPadding şifreleri,
getOutputSize()
'ten daha doğru arabellek boyutları döndürür. - TLS_FALLBACK_SCSV şifre paketi, maksimum protokolü TLS 1.2 veya üstü olan bağlantı girişimlerinden çıkarılır. TLS sunucu uygulamalarında yapılan iyileştirmeler nedeniyle, TLS harici yedekleme denemesi yapmanızı önermiyoruz. Bunun yerine TLS sürüm pazarlığı yapmanızı öneririz.
- ChaCha20-Poly1305, ChaCha20/Poly1305/NoPadding için bir takma addır.
- Sonunda nokta bulunan ana makine adları geçerli SNI ana makine adları olarak kabul edilmez.
- Sertifika yanıtları için imzalama anahtarı seçilirken CertificateRequest'teki supported_signature_algorithms uzantısına uyulur.
- Android Keystore'dakiler gibi opak imzalama anahtarları, TLS'de RSA-PSS imzalarıyla kullanılabilir.
HTTPS bağlantısı değişiklikleri
Android 10 çalıştıran bir uygulama setSSLSocketFactory()
değerine null gönderirse IllegalArgumentException
oluşur. Önceki sürümlerde, setSSLSocketFactory()
parametresine null göndermek, mevcut varsayılan fabrikayı göndermekle aynı etkiye sahipti.
Android 11
SSL soketlerinde varsayılan olarak Conscrypt SSL motoru kullanılır
Android'in varsayılan SSLSocket uygulaması Conscrypt
'e dayanır. Android 11'den beri bu uygulama, Conscrypt'in SSLEngine'i temel alınarak dahili olarak oluşturulmaktadır.