Secure Socket Layer (SSL) yang sekarang secara teknis dikenal sebagai Transport Layer Security (TLS) adalah elemen penyusun umum untuk komunikasi terenkripsi antara klien dan server. Ada kemungkinan bahwa aplikasi salah menggunakan SSL sehingga objek berbahaya mungkin mencegat data aplikasi melalui jaringan. Untuk membantu memastikan hal ini tidak terjadi pada aplikasi Anda, artikel ini membahas masalah umum saat menggunakan protokol jaringan yang aman dan mengatasi beberapa kekhawatiran yang lebih besar tentang penggunaan Public-Key Infrastructure atau IKP.
Anda juga harus membaca Ringkasan Keamanan Android serta Ringkasan Izin.
Konsep
Dalam skenario penggunaan SSL umum, server dikonfigurasi dengan sertifikat yang berisi kunci publik dan kunci pribadi yang cocok. Sebagai bagian dari handshake antara klien dan server SSL, server membuktikan kepemilikan kunci pribadi dengan menandatangani sertifikat dengan kriptografi kunci publik.
Namun, siapa pun dapat menghasilkan sertifikat dan kunci pribadi sendiri, sehingga handshake yang sederhana tidak membuktikan apa pun tentang server selain bahwa server mengetahui kunci pribadi yang cocok dengan kunci publik sertifikat. Salah satu cara untuk memecahkan masalah ini adalah dengan meminta klien memiliki kumpulan satu atau beberapa sertifikat yang dipercayainya. Jika sertifikat tidak ada dalam kumpulan tersebut, server tidak dapat dipercaya.
Ada beberapa kelemahan dari pendekatan sederhana ini. Server harus dapat mengupgrade ke kunci yang lebih kuat dari waktu ke waktu ("rotasi kunci"), yang menggantikan kunci publik dalam sertifikat dengan yang baru. Sayangnya, sekarang aplikasi klien harus diupdate karena perubahan konfigurasi server. Hal ini akan bermasalah jika server tidak di bawah kontrol developer aplikasi, misalnya jika server adalah layanan web pihak ketiga. Pendekatan ini juga memiliki masalah jika aplikasi harus berkomunikasi ke server arbitrer seperti browser web atau aplikasi email.
Untuk mengatasi kelemahan ini, server biasanya dikonfigurasi dengan sertifikat dari penerbit populer yang disebut Certificate Authority (CA). Platform host biasanya berisi daftar CA terkenal yang dipercaya. Hingga Android 4.2 (Jelly Bean), Android saat ini berisi lebih dari 100 CA yang diupdate dalam setiap rilis. Seperti halnya pada server, CA memiliki sertifikat dan kunci pribadi. Saat menerbitkan sertifikat untuk server, CA menandatangani sertifikat server menggunakan kunci pribadinya. Klien kemudian dapat memverifikasi bahwa server memiliki sertifikat yang diterbitkan oleh CA yang dikenal oleh platform.
Namun, meski bisa memecahkan beberapa masalah, penggunaan CA memunculkan masalah lain. Karena CA menerbitkan sertifikat untuk banyak server, Anda masih memerlukan beberapa cara untuk memastikan bahwa Anda berkomunikasi dengan server yang diinginkan. Untuk mengatasi masalah ini, sertifikat yang dikeluarkan oleh CA mengidentifikasi server dengan nama spesifik seperti gmail.com atau kumpulan karakter pengganti dari host seperti *.google.com.
Contoh berikut akan membuat konsep ini sedikit lebih konkret. Pada cuplikan di bawah ini
dari suatu command line, perintah s_client
alat openssl
mempertimbangkan informasi sertifikat server Wikipedia. Tindakan ini
menentukan port 443 karena merupakan setelan default untuk HTTPS. Perintah tersebut mengirimkan
output dari openssl s_client
ke openssl x509
, yang memformat informasi
tentang sertifikat yang sesuai dengan standar X.509. Secara khusus,
perintah tersebut meminta subjek yang berisi informasi nama server
dan penerbit yang mengidentifikasi CA.
$ openssl s_client -connect wikipedia.org:443 | openssl x509 -noout -subject -issuer subject= /serialNumber=sOrr2rKpMVP70Z6E9BT5reY008SJEdYv/C=US/O=*.wikipedia.org/OU=GT03314600/OU=See www.rapidssl.com/resources/cps (c)11/OU=Domain Control Validated - RapidSSL(R)/CN=*.wikipedia.org issuer= /C=US/O=GeoTrust, Inc./CN=RapidSSL CA
Anda dapat melihat bahwa sertifikat diterbitkan oleh RapidSSL CA untuk server yang cocok dengan *.wikipedia.org.
Contoh HTTPS
Dengan anggapan bahwa Anda memiliki sebuah server web dengan sertifikat yang diterbitkan oleh CA yang umum dikenal, Anda dapat membuat permintaan aman dengan kode semudah ini:
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);
Ya, benar-benar bisa semudah itu. Jika ingin menyesuaikan permintaan HTTP, Anda dapat melakukan transmisi ke
HttpURLConnection
. Dokumentasi Android untuk
HttpURLConnection
memiliki contoh lebih lanjut tentang cara menangani header
respons dan permintaan, memposting konten, mengelola cookie, menggunakan proxy, men-cache respons,
dan sebagainya. Namun, untuk detail cara memverifikasi sertifikat dan hostname, framework
Android akan menanganinya untuk Anda melalui API ini.
Ini adalah tempat yang Anda inginkan jika keadaan memungkinkan. Di bawah ini adalah beberapa pertimbangan lainnya.
Masalah umum dalam memverifikasi sertifikat server
Anggap saja bukannya menerima konten dari getInputStream()
, malah menampilkan pengecualian:
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)
Hal ini dapat terjadi karena beberapa alasan, seperti:
- CA yang mengeluarkan sertifikat server tidak dikenal
- Sertifikat server tidak ditandatangani oleh CA, tetapi ditandatangani sendiri
- CA perantara tidak ditemukan pada konfigurasi server
Bagian berikut membahas cara mengatasi masalah ini sambil menjaga koneksi Anda ke server tetap aman.
Otoritas sertifikat tidak dikenal
Dalam kasus ini, SSLHandshakeException
terjadi
karena Anda memiliki CA yang tidak dipercaya oleh sistem. Bisa jadi karena
Anda memiliki sertifikat dari CA baru yang belum dipercaya oleh Android, atau aplikasi Anda
berjalan pada versi lama tanpa CA. Sering kali CA tidak dikenal karena bukan CA publik,
tetapi CA pribadi yang dikeluarkan oleh organisasi seperti pemerintah, perusahaan,
atau lembaga pendidikan untuk penggunaan mereka sendiri.
Untungnya, Anda dapat melatih HttpsURLConnection
agar memercayai kumpulan CA tertentu. Prosedurnya
bisa sedikit rumit. Di bawah ini adalah contoh yang mengambil CA tertentu dari
InputStream
, menggunakannya untuk membuat KeyStore
,
yang kemudian digunakan untuk membuat dan menginisialisasi
TrustManager
. TrustManager
adalah yang digunakan
sistem untuk memvalidasi sertifikat dari
server—dengan membuat satu dari KeyStore
bersama satu atau beberapa CA—dan
akan menjadi satu-satunya CA yang dipercaya oleh TrustManager
.
Dengan pertimbangan TrustManager
baru,
contoh akan menginisialisasi SSLContext
baru yang menyediakan
SSLSocketFactory
yang dapat Anda gunakan untuk mengganti
SSLSocketFactory
default dari
HttpsURLConnection
. Dengan cara ini,
koneksi akan menggunakan CA Anda untuk validasi sertifikat.
Berikut adalah contoh lengkap penggunaan CA organisasi dari University of Washington:
Kotlin
// Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) val cf: CertificateFactory = CertificateFactory.getInstance("X.509") // From https://www.washington.edu/itconnect/security/ca/load-der.crt val caInput: InputStream = BufferedInputStream(FileInputStream("load-der.crt")) val ca: X509Certificate = caInput.use { cf.generateCertificate(it) as X509Certificate } System.out.println("ca=" + ca.subjectDN) // Create a KeyStore containing our trusted CAs val keyStoreType = KeyStore.getDefaultType() val keyStore = KeyStore.getInstance(keyStoreType).apply { load(null, null) setCertificateEntry("ca", ca) } // Create a TrustManager that trusts the CAs inputStream our KeyStore val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm() val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply { init(keyStore) } // Create an SSLContext that uses our TrustManager val context: SSLContext = SSLContext.getInstance("TLS").apply { init(null, tmf.trustManagers, null) } // Tell the URLConnection to use a SocketFactory from our SSLContext val url = URL("https://certs.cac.washington.edu/CAtest/") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.sslSocketFactory = context.socketFactory val inputStream: InputStream = urlConnection.inputStream copyInputStreamToOutputStream(inputStream, System.out)
Java
// Load CAs from an InputStream // (could be from a resource or ByteArrayInputStream or ...) CertificateFactory cf = CertificateFactory.getInstance("X.509"); // From https://www.washington.edu/itconnect/security/ca/load-der.crt InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt")); Certificate ca; try { ca = cf.generateCertificate(caInput); System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); } finally { caInput.close(); } // Create a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // Create an SSLContext that uses our TrustManager SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); // Tell the URLConnection to use a SocketFactory from our SSLContext URL url = new URL("https://certs.cac.washington.edu/CAtest/"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
Dengan TrustManager
kustom yang mengetahui CA Anda,
sistem dapat memvalidasi bahwa
sertifikat server Anda berasal dari penerbit tepercaya.
Perhatian:
Banyak situs menjelaskan solusi alternatif yang buruk dengan menginstal
TrustManager
yang tidak berfungsi sama sekali. Jika melakukannya, Anda sama saja dengan
tidak mengenkripsi komunikasi, karena siapa pun dapat menyerang pengguna Anda di hotspot Wi-Fi publik
menggunakan trik DNS untuk mengirimkan traffic pengguna Anda
melalui proxy mereka sendiri yang berpura-pura menjadi server Anda. Kemudian penyerang dapat
merekam sandi dan data pribadi lainnya. Cara ini dapat dilakukan karena penyerang dapat membuat
sertifikat dan, tanpa TrustManager
yang benar-benar
melakukan validasi bahwa sertifikat tersebut berasal dari sumber
tepercaya, aplikasi Anda bisa saja berkomunikasi dengan siapa pun. Jadi, jangan lakukan hal ini, bahkan untuk sementara sekalipun. Anda selalu
dapat membuat aplikasi memercayai penerbit sertifikat server. Jadi, lakukan saja cara ini.
Sertifikat server ditandatangani sendiri
Kasus kedua dari SSLHandshakeException
adalah
karena sertifikat yang ditandatangani sendiri yang berarti server berperilaku sebagai CA-nya sendiri.
Ini mirip dengan certificate authority yang tidak dikenal, sehingga Anda dapat menggunakan
pendekatan yang sama dari bagian sebelumnya.
Anda dapat membuat TrustManager
Anda sendiri,
kali ini dengan memercayai sertifikat server secara langsung. Cara ini memiliki semua
kekurangan yang sebelumnya telah dibahas karena mengikat aplikasi secara langsung ke sertifikat, tetapi cara ini dapat dilakukan
dengan aman. Namun, Anda harus berhati-hati untuk memastikan sertifikat yang ditandatangani sendiri memiliki
kunci yang cukup kuat. Sejak 2012, tanda tangan RSA 2048-bit dengan eksponen 65537 yang habis masa berlakunya
tahunan dapat diterima. Saat merotasi kunci, sebaiknya periksa rekomendasi dari
otoritas (seperti NIST) tentang apa yang dapat diterima.
Certificate authority perantara tidak ada
Kasus ketiga SSLHandshakeException
terjadi karena CA perantara tidak ada. Sebagian besar
CA publik tidak menandatangani sertifikat server secara langsung. Sebagai gantinya, sertifikat CA utama akan digunakan,
yang disebut sebagai CA root, untuk menandatangani CA perantara. CA publik melakukan ini sehingga CA root dapat disimpan
secara offline untuk mengurangi risiko disusupi. Namun, sistem operasi seperti Android biasanya
hanya memercayai CA root secara langsung, yang menyisakan sedikit celah kepercayaan antara sertifikat
server—yang ditandatangani CA perantara—dan pemverifikasi sertifikat,
yang mengetahui CA root. Untuk mengatasi ini,
server tidak hanya mengirimkan sertifikatnya kepada klien selama handshake SSL, melainkan
juga rantai sertifikat dari CA server melalui perantara yang diperlukan untuk mencapai
CA root tepercaya.
Untuk melihat bagaimana praktiknya, berikut rantai sertifikat mail.google.com
seperti yang dilihat oleh perintah s_client
openssl
:
$ 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 ---
Ini menunjukkan bahwa server mengirimkan sertifikat untuk mail.google.com yang dikeluarkan oleh Thawte SGC CA, yang merupakan CA perantara, dan sertifikat kedua untuk Thawte SGC CA diterbitkan oleh Verisign CA yang merupakan CA utama yang dipercaya oleh Android.
Namun, tidak jarang dilakukan konfigurasi server dengan tidak menyertakan CA perantara yang diperlukan. Misalnya, berikut adalah server yang dapat menyebabkan error dalam browser Android dan pengecualian dalam aplikasi Android:
$ 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 ---
Yang menarik untuk diperhatikan di sini adalah bahwa mengunjungi server ini di sebagian besar browser desktop tidak menyebabkan error seperti yang ditimbulkan oleh CA yang tidak dikenal sama sekali atau sertifikat server yang ditandatangani sendiri. Ini dikarenakan sebagian besar cache browser desktop memercayai CA perantara seiring berjalannya waktu Setelah mengunjungi dan mempelajari CA perantara dari satu situs, browser tidak perlu lagi memasukkan CA perantara dalam rantai sertifikat di waktu mendatang.
Beberapa situs melakukan ini dengan sengaja untuk server web sekunder yang digunakan untuk menyediakan resource. Misalnya, mungkin halaman HTML utamanya disalurkan oleh server dengan rantai sertifikat penuh, tetapi menggunakan server untuk resource seperti gambar, CSS, atau JavaScript tanpa CA, yang tujuannya mungkin untuk menghemat bandwidth. Sayangnya, terkadang server ini memberikan layanan web yang Anda coba panggil dari aplikasi Android, sehingga memberikan dampak negatif.
Ada dua pendekatan untuk memecahkan masalah ini:
- Mengonfigurasi server untuk menyertakan CA perantara dalam rantai server. Sebagian besar CA memberikan dokumentasi tentang cara melakukan hal ini untuk semua server web umum. Ini adalah satu-satunya pendekatan jika Anda menginginkan situs bekerja dengan browser Android default setidaknya melalui Android 4.2.
- Atau, perlakukan
CA perantara seperti CA tidak dikenal lainnya, dan buat
TrustManager
untuk memercayainya secara langsung, seperti yang dilakukan di dua bagian sebelumnya.
Masalah umum dengan verifikasi hostname.
Seperti yang disebutkan di awal artikel ini, ada dua bagian kunci untuk memverifikasi koneksi SSL. Pertama adalah memverifikasi sertifikat tersebut dari sumber tepercaya, yang merupakan fokus dari bagian sebelumnya. Fokus bagian ini adalah bagian kedua: memastikan server yang Anda hubungi menyajikan sertifikat yang benar. Jika tidak benar, Anda biasanya akan melihat error seperti ini:
java.io.IOException: Hostname 'example.com' was not verified at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446) 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)
Salah satu alasan terjadinya hal ini dikarenakan adanya error konfigurasi server. Server
dikonfigurasi dengan sertifikat yang tidak memiliki kolom subjek atau kolom nama alternatif subjek
yang cocok dengan server yang sedang Anda coba hubungi. Dimungkinkan untuk memiliki satu sertifikat yang digunakan
dengan banyak server yang berbeda. Misalnya, dengan melihat sertifikat google.com beserta
s_client -connect google.com:443 | openssl x509 -text
openssl
, Anda dapat melihat subjek
yang mendukung *.google.com tetapi juga nama alternatif subjek untuk *.youtube.com,
*.android.com, dan lainnya. Error ini terjadi hanya jika nama server yang ingin Anda hubungi
tidak terdaftar oleh sertifikat sebagai dapat diterima.
Sayangnya, hal ini juga bisa terjadi karena alasan lain: hosting virtual. Ketika berbagi server untuk lebih dari satu hostname dengan HTTP, server web dapat membedakannya dari permintaan HTTP/1.1 yang menargetkan hostname yang dicari klien. Sayangnya, berbagi server menjadi rumit dengan HTTPS karena server harus mengetahui sertifikat mana yang ditampilkan sebelum melihat permintaan HTTP. Untuk mengatasinya, versi SSL yang lebih baru, khususnya TLSv.1.0 dan setelahnya, mendukung Server Name Indication (SNI), yang memungkinkan klien SSL untuk menentukan hostname yang dimaksudkan ke server sehingga sertifikat yang tepat dapat ditampilkan.
Untungnya, HttpsURLConnection
mendukung
SNI sejak Android 2.3. Salah satu solusi
jika Anda perlu mendukung Android 2.2 (dan versi yang lebih lama) adalah dengan menyiapkan
sebuah host virtual alternatif pada port unik sehingga jelas sertifikat server mana yang ditampilkan.
Alternatif yang lebih jelas lagi adalah mengganti HostnameVerifier
dengan yang tidak menggunakan
hostname dari host virtual Anda, tetapi yang ditampilkan oleh server secara default.
Perhatian: Mengganti HostnameVerifier
dapat sangat berbahaya jika host virtual lain
tidak berada di bawah kendali Anda, karena serangan man in the middle dapat mengarahkan traffic ke
server lain tanpa sepengetahuan Anda.
Jika Anda masih yakin ingin mengganti verifikasi hostname, berikut adalah contoh
yang menggantikan pemverifikasi untuk satu URLConnection
dengan yang masih memverifikasi bahwa hostname setidaknya sesuai yang diharapkan oleh aplikasi:
Kotlin
// Create an HostnameVerifier that hardwires the expected hostname. // Note that is different than the URL's hostname: // example.com versus example.org val hostnameVerifier = HostnameVerifier { _, session -> HttpsURLConnection.getDefaultHostnameVerifier().run { verify("example.com", session) } } // Tell the URLConnection to use our HostnameVerifier val url = URL("https://example.org/") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.hostnameVerifier = hostnameVerifier val inputStream: InputStream = urlConnection.inputStream copyInputStreamToOutputStream(inputStream, System.out)
Java
// Create an HostnameVerifier that hardwires the expected hostname. // Note that is different than the URL's hostname: // example.com versus example.org HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); return hv.verify("example.com", session); } }; // Tell the URLConnection to use our HostnameVerifier URL url = new URL("https://example.org/"); HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection(); urlConnection.setHostnameVerifier(hostnameVerifier); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
Namun ingat, jika Anda memutuskan untuk mengganti verifikasi hostname, terutama karena hosting virtual, hal itu tetap sangat berbahaya jika host virtual lain tidak di bawah kendali Anda dan Anda sebaiknya menemukan pengaturan hosting alternatif sehingga dapat terhindar dari masalah ini.
Peringatan tentang penggunaan SSLSocket secara langsung
Sejauh ini, contoh berfokus pada HTTPS menggunakan HttpsURLConnection
.
Terkadang aplikasi perlu menggunakan SSL yang terpisah dengan HTTP. Misalnya, aplikasi email mungkin menggunakan varian SSL
dari SMTP, POP3, atau IMAP. Dalam kasus tersebut, aplikasi ingin menggunakan SSLSocket
secara langsung, seperti yang dilakukan HttpsURLConnection
secara internal.
Teknik yang dijelaskan sejauh ini
untuk menangani masalah verifikasi sertifikat juga berlaku untuk SSLSocket
.
Bahkan, saat menggunakan TrustManager
kustom, SSLSocketFactory
akan
diteruskan ke HttpsURLConnection
.
Jadi, jika Anda perlu menggunakan TrustManager
kustom beserta
SSLSocket
, ikuti
langkah-langkah yang sama dan gunakan SSLSocketFactory
untuk membuat
SSLSocket
Anda.
Perhatian:
SSLSocket
tidak menjalankan verifikasi hostname. Cara
melakukan verifikasi hostname sendiri tergantung aplikasi Anda, sebaiknya panggil getDefaultHostnameVerifier()
bersama hostname yang diharapkan. Selanjutnya,
berhati-hatilah bahwa HostnameVerifier.verify()
tidak menampilkan pengecualian pada error melainkan menampilkan hasil boolean yang harus
diperiksa secara eksplisit.
Berikut adalah contoh yang menunjukkan cara untuk melakukannya. Contoh ini menunjukkan bahwa saat menghubungkan ke gmail.com port 443 tanpa dukungan SNI, Anda akan menerima sertifikat untuk mail.google.com. Hal ini diharapkan dalam kasus ini, jadi pastikan bahwa sertifikat tersebut memang untuk mail.google.com:
Kotlin
// Open SSLSocket directly to gmail.com val socket: SSLSocket = SSLSocketFactory.getDefault().run { createSocket("gmail.com", 443) as SSLSocket } val session = socket.session // Verify that the certicate hostname is for mail.google.com // This is due to lack of SNI support in the current SSLSocket. HttpsURLConnection.getDefaultHostnameVerifier().run { if (!verify("mail.google.com", session)) { throw SSLHandshakeException("Expected mail.google.com, found ${session.peerPrincipal} ") } } // At this point SSLSocket performed certificate verification and // we have performed hostname verification, so it is safe to proceed. // ... use socket ... socket.close()
Java
// Open SSLSocket directly to gmail.com SocketFactory sf = SSLSocketFactory.getDefault(); SSLSocket socket = (SSLSocket) sf.createSocket("gmail.com", 443); HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); SSLSession s = socket.getSession(); // Verify that the certicate hostname is for mail.google.com // This is due to lack of SNI support in the current SSLSocket. if (!hv.verify("mail.google.com", s)) { throw new SSLHandshakeException("Expected mail.google.com, " "found " + s.getPeerPrincipal()); } // At this point SSLSocket performed certificate verification and // we have performed hostname verification, so it is safe to proceed. // ... use socket ... socket.close();
Daftar yang tidak diizinkan
SSL sangat bergantung pada CA untuk menerbitkan sertifikat hanya bagi pemilik server dan domain yang sudah diverifikasi dengan benar. Dalam kasus yang jarang terjadi, CA dicurangi atau, jika Comodo atau DigiNotar dilanggar, sehingga sertifikat untuk hostname dikeluarkan untuk orang lain selain pemilik server atau domain.
Untuk mengurangi risiko ini, Android memiliki kemampuan untuk membuat daftar pemblokiran sertifikat tertentu atau bahkan seluruh CA. Meskipun daftar ini secara historis di-build ke dalam sistem operasi, mulai Android 4.2, daftar ini dapat diperbarui dari jarak jauh untuk menangani penyusupan di masa mendatang.
Memasang pin
Aplikasi dapat lebih melindungi dirinya dari sertifikat palsu yang dikeluarkan dari teknik yang dikenal sebagai pemasangan pin. Teknik ini pada dasarnya menggunakan contoh yang diberikan dalam kasus CA tidak dikenal di atas untuk membatasi CA yang dipercaya aplikasi dalam kelompok kecil yang dikenal untuk digunakan oleh server aplikasi. Hal ini akan mencegah penyusupan satu CA dari lebih dari 100 CA lainnya dalam sistem yang dapat mengakibatkan penerobosan saluran aman aplikasi.
Sertifikat klien
Artikel ini berfokus pada pengguna SSL untuk mengamankan komunikasi dengan server. SSL juga
mendukung konsep sertifikat klien yang memungkinkan server memvalidasi identitas
klien. Meskipun di luar cakupan artikel ini, teknik yang terlibat mirip dengan penetapan
TrustManager
kustom.
Lihat diskusi tentang membuat KeyManager
kustom dalam dokumentasi untuk
HttpsURLConnection
.
Nogotofail: Alat pengujian keamanan traffic jaringan
Nogotofail adalah alat yang memberi Anda kemudahan untuk mengonfirmasi bahwa aplikasi Anda aman terhadap kerentanan dan kesalahan konfigurasi TLS/SSL yang dikenal. Ini adalah alat yang otomatis, kuat, dan skalabel untuk pengujian masalah keamanan jaringan pada perangkat apa pun yang dapat dilalui traffic jaringan.
Nogotofail bermanfaat untuk tiga kasus penggunaan utama:
- Menemukan bug dan kerentanan.
- Memverifikasi perbaikan dan memperhatikan regresi.
- Memahami traffic apa yang akan dihasilkan aplikasi dan perangkat.
Nogotofail dapat digunakan untuk Android, iOS, Linux, Windows, Chrome OS, OSX, bahkan semua perangkat yang Anda gunakan untuk terhubung ke Internet. Terdapat klien yang mudah digunakan untuk mengonfigurasi setelan serta mendapatkan notifikasi di Android dan Linux, serta mesin serangan yang dapat diterapkan sebagai router, server VPN, atau proxy.
Anda dapat mengakses fitur ini di project open source Nogotofail.