Keamanan dengan HTTPS dan SSL

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:

  1. CA yang mengeluarkan sertifikat server tidak dikenal
  2. Sertifikat server tidak ditandatangani oleh CA, tetapi ditandatangani sendiri
  3. 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.