Keamanan dengan protokol jaringan

Interaksi terenkripsi server klien menggunakan Transport Layer Security (TLS) untuk melindungi data aplikasi Anda.

Artikel ini membahas praktik terbaik terkait protokol jaringan yang aman dan pertimbangan Infrastruktur Kunci Publik (PKI). Baca Ringkasan Keamanan Android dan Ringkasan Izin untuk mengetahui detail selengkapnya.

Konsep

Server dengan sertifikat TLS memiliki kunci publik dan kunci pribadi yang cocok. Server menggunakan kriptografi kunci publik untuk menandatangani sertifikatnya selama TLS handshake.

Handshake sederhana hanya membuktikan bahwa server mengetahui kunci pribadi sertifikat. Untuk mengatasi situasi ini, biarkan klien memercayai beberapa sertifikat. Server tertentu tidak dapat dipercaya jika sertifikatnya tidak muncul dalam kumpulan sertifikat tepercaya sisi klien.

Namun, server mungkin menggunakan rotasi kunci untuk mengubah kunci publik sertifikatnya dengan kunci baru. Perubahan konfigurasi server mengharuskan aplikasi klien diupdate. Jika server adalah layanan web pihak ketiga, seperti browser web atau aplikasi email, akan lebih sulit untuk mengetahui kapan harus mengupdate aplikasi klien.

Server biasanya mengandalkan sertifikat Certificate Authorities (CA) untuk menerbitkan sertifikat, yang membuat konfigurasi sisi klien lebih stabil dari waktu ke waktu. CA menandatangani sertifikat server menggunakan kunci pribadinya. Selanjutnya, klien dapat memeriksa apakah server memiliki sertifikat CA yang dikenal oleh platform.

CA tepercaya biasanya tercantum di platform host. Android 8.0 (level API 26) menyertakan lebih dari 100 CA yang diupdate dalam setiap versi dan tidak berubah antar-perangkat.

Aplikasi klien memerlukan mekanisme untuk memverifikasi server karena CA menawarkan sertifikat untuk banyak server. Sertifikat CA mengidentifikasi server menggunakan nama tertentu, seperti gmail.com, atau menggunakan karakter pengganti, seperti *.google.com.

Untuk melihat informasi sertifikat server situs, gunakan perintah s_client di alat openssl dengan meneruskan nomor port. Secara default, HTTPS menggunakan port 443.

Perintah ini mengirimkan output openssl s_client ke openssl x509 yang memformat informasi sertifikat dalam standar X.509. Perintah ini meminta topik (nama server) dan penerbit (CA).

openssl s_client -connect WEBSITE-URL:443 | \
  openssl x509 -noout -subject -issuer

Contoh HTTPS

Dengan asumsi bahwa Anda memiliki server web dengan sertifikat yang diterbitkan oleh CA terkenal, Anda dapat membuat permintaan aman seperti yang ditunjukkan dalam kode berikut:

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);

Untuk menyesuaikan permintaan HTTP, transmisikan ke HttpURLConnection. Dokumentasi HttpURLConnection Android mencakup contoh untuk menangani header permintaan dan respons, memublikasikan konten, mengelola cookie, menggunakan proxy, menyimpan respons dalam cache, dan lainnya. Framework Android memverifikasi sertifikat dan nama host menggunakan API ini.

Gunakan API ini jika memungkinkan. Bagian berikut membahas masalah umum yang memerlukan solusi berbeda.

Masalah umum dalam memverifikasi sertifikat server

Misalkan getInputStream() menampilkan pengecualian, bukan menampilkan konten:

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 di konfigurasi server.

Bagian berikut membahas cara mengatasi masalah ini sembari menjaga koneksi Anda ke server tetap aman.

Otoritas sertifikat tidak dikenal

SSLHandshakeException muncul karena sistem tidak memercayai CA. Mungkin ini karena Anda memiliki sertifikat dari CA baru yang tidak dipercaya oleh Android atau karena aplikasi Anda beroperasi pada versi sebelumnya tanpa CA. Karena sifatnya pribadi, CA jarang diketahui. Sering kali CA tidak dikenal karena bukan CA publik, tetapi CA pribadi yang dikeluarkan oleh organisasi seperti pemerintah, perusahaan, atau institusi pendidikan untuk penggunaan mereka sendiri.

Untuk memercayai CA kustom tanpa perlu mengubah kode aplikasi Anda, ubah Konfigurasi Keamanan Jaringan.

Perhatian: Banyak situs menjelaskan solusi alternatif yang buruk dengan menginstal TrustManager yang tidak berfungsi sama sekali. Dengan melakukan hal ini, pengguna akan rentan terhadap serangan saat menggunakan hotspot Wi-Fi publik karena penyerang dapat menggunakan trik DNS untuk mengirimkan traffic pengguna melalui proxy yang berpura-pura menjadi server Anda. Kemudian penyerang dapat merekam sandi dan data pribadi lainnya. Cara ini berfungsi karena penyerang dapat membuat sertifikat, dan tanpa TrustManager yang memvalidasi bahwa sertifikat tersebut berasal dari sumber tepercaya, Anda tidak dapat memblokir serangan semacam ini. Jadi, jangan lakukan hal ini, bahkan untuk sementara sekalipun. Sebagai gantinya, buat aplikasi Anda memercayai penerbit sertifikat server.

Sertifikat server ditandatangani sendiri

Kedua, SSLHandshakeException dapat terjadi karena sertifikat yang ditandatangani sendiri, membuat server menjadi CA-nya sendiri. Ini serupa dengan certificate authority yang tidak dikenal, jadi ubah Konfigurasi Keamanan Jaringan aplikasi Anda agar memercayai sertifikat yang ditandatangani sendiri.

Intermediate certificate authority tidak ada

Ketiga, SSLHandshakeException terjadi karena CA perantara tidak ada. CA publik jarang menandatangani sertifikat server. Sebaliknya, CA root menandatangani CA perantara.

Untuk mengurangi risiko penyusupan, CA membuat CA root tetap offline. Namun, sistem operasi seperti Android biasanya hanya memercayai CA root secara langsung sehingga menyisakan sedikit kesenjangan kepercayaan antara sertifikat server—yang ditandatangani CA perantara—dan pemverifikasi sertifikat, yang mengenali CA root.

Untuk menghapus kesenjangan kepercayaan ini, server mengirimkan rantai sertifikat dari CA server melalui perantara apa pun ke CA root tepercaya selama TLS handshake.

Misalnya, berikut adalah 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
---

Rantai sertifikat 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, server mungkin tidak dikonfigurasi untuk menyertakan CA perantara yang diperlukan. Misalnya, berikut adalah server yang dapat menyebabkan kesalahan 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
---

Tidak seperti CA yang tidak dikenal atau sertifikat server yang ditandatangani sendiri, sebagian besar browser desktop tidak menghasilkan error saat berkomunikasi dengan server ini. Cache browser desktop memercayai CA perantara. Setelah mempelajari CA perantara dari satu situs, browser tidak akan membutuhkannya lagi dalam rantai sertifikat.

Beberapa situs sengaja melakukan hal ini untuk server web sekunder penyaluran resource. Untuk menghemat bandwidth, situs-situs itu mungkin menayangkan halaman HTML utama dari server dengan rantai sertifikat lengkap, kecuali gambar, CSS, dan JavaScript tanpa CA. Sayangnya, terkadang server ini dapat menyediakan layanan web yang ingin Anda jangkau dari aplikasi Android, yang tidak terlalu toleran.

Untuk memperbaiki masalah ini, konfigurasi server untuk menyertakan CA perantara dalam rantai server. Sebagian besar CA memberikan petunjuk tentang cara melakukannya untuk server web umum.

Peringatan tentang penggunaan SSLSocket secara langsung

Sejauh ini, contoh berfokus pada HTTPS menggunakan HttpsURLConnection. Terkadang aplikasi perlu menggunakan TLS yang terpisah dari HTTPS. Misalnya, aplikasi email mungkin menggunakan varian TLS dari SMTP, POP3, atau IMAP. Dalam kasus tersebut, aplikasi dapat menggunakan SSLSocket secara langsung, seperti yang dilakukan HttpsURLConnection secara internal.

Teknik yang telah 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 nama host. Cara melakukan verifikasi nama host sendiri bergantung pada aplikasi Anda, tetapi sebaiknya dengan memanggil getDefaultHostnameVerifier() beserta nama host yang diharapkan. Selain itu, perhatikan bahwa HostnameVerifier.verify() tidak menampilkan pengecualian pada error. Sebagai gantinya, metode ini menampilkan hasil boolean yang harus Anda periksa secara eksplisit.

CA yang diblokir

TLS mengandalkan CA untuk menerbitkan sertifikat, hanya bagi pemilik server dan domain terverifikasi. Dalam kasus yang jarang terjadi, CA dicurangi atau, dalam kasus Comodo atau DigiNotar, dilanggar, sehingga sertifikat untuk nama host dikeluarkan bagi orang lain selain pemilik server atau domain.

Untuk mengurangi risiko ini, Android memiliki kemampuan untuk menambahkan sertifikat tertentu atau bahkan seluruh CA ke daftar tolak. Meskipun daftar ini secara historis dibangun ke dalam sistem operasi, mulai Android 4.2, daftar ini dapat diupdate dari jarak jauh untuk menangani penyusupan di masa mendatang.

Membatasi aplikasi Anda ke sertifikat tertentu

Perhatian: Penyematan sertifikat, yaitu praktik pembatasan sertifikat yang dianggap valid untuk aplikasi Anda ke sertifikat yang telah Anda izinkan sebelumnya, tidak direkomendasikan untuk aplikasi Android. Perubahan konfigurasi server pada masa mendatang, seperti beralih ke CA lain, akan merender aplikasi dengan sertifikat tersemat yang tidak dapat terhubung ke server tanpa menerima update software klien.

Jika Anda ingin membatasi aplikasi agar hanya menerima sertifikat yang ditentukan, Anda harus menyertakan beberapa pin cadangan, termasuk setidaknya satu kunci yang sepenuhnya Anda kendalikan, dan periode habis masa berlaku yang cukup singkat untuk mencegah masalah kompatibilitas. Konfigurasi Keamanan Jaringan menyediakan penyematan dengan kemampuan ini.

Sertifikat klien

Artikel ini berfokus pada penggunaan TLS untuk mengamankan komunikasi dengan server. TLS 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.

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 umum. Inilah alat yang otomatis, kuat, dan skalabel untuk pengujian masalah keamanan jaringan di 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 berfungsi untuk Android, iOS, Linux, Windows, ChromeOS, macOS, dan bahkan perangkat apa pun yang Anda gunakan untuk terhubung ke internet. Klien dapat mengonfigurasi setelan serta mendapatkan notifikasi di Android dan Linux, sedangkan mesin penyerang itu sendiri dapat di-deploy sebagai router, server VPN, atau proxy.

Anda dapat mengakses fitur ini di project open source Nogotofail.

Update untuk SSL dan TLS

Android 10

Beberapa browser, seperti Google Chrome, memungkinkan pengguna untuk memilih sertifikat ketika server TLS mengirimkan pesan permintaan sertifikat sebagai bagian dari TLS handshake. Mulai Android 10, objek KeyChain mengikuti parameter spesifikasi kunci dan penerbit saat memanggil KeyChain.choosePrivateKeyAlias() untuk menampilkan prompt pemilihan sertifikat kepada pengguna. Secara khusus, prompt ini tidak berisi pilihan yang tidak mematuhi spesifikasi server.

Jika tidak ada sertifikat yang bisa dipilih pengguna, seperti ketika tidak ada sertifikat yang cocok dengan spesifikasi server atau perangkat tidak memiliki sertifikat yang terinstal, prompt pemilihan sertifikat tidak akan muncul.

Selain itu, di Android 10 atau yang lebih tinggi, Anda tidak harus menyetel kunci layar perangkat untuk mengimpor kunci atau sertifikat CA ke objek KeyChain.

TLS 1.3 diaktifkan secara default

Di Android 10 dan yang lebih tinggi, TLS 1.3 diaktifkan secara default untuk semua koneksi TLS. Berikut adalah beberapa detail penting tentang implementasi TLS 1.3 kami:

  • Cipher suite TLS 1.3 tidak bisa disesuaikan. Cipher suite TLS 1.3 yang didukung selalu diaktifkan saat TLS 1.3 diaktifkan. Upaya apa pun untuk menonaktifkannya dengan memanggil setEnabledCipherSuites() akan diabaikan.
  • Saat TLS 1.3 dinegosiasikan, objek HandshakeCompletedListener akan dipanggil sebelum sesi ditambahkan ke cache sesi. (Pada TLS 1.2 dan versi sebelumnya, objek ini dipanggil setelah sesi ditambahkan ke cache sesi.)
  • Dalam beberapa situasi saat instance SSLEngine menampilkan SSLHandshakeException di versi Android sebelumnya, instance ini akan menampilkan SSLProtocolException di Android 10 dan yang lebih tinggi.
  • Mode 0-RTT tidak didukung.

Jika diinginkan, Anda bisa mendapatkan SSLContext yang menonaktifkan TLS 1.3 dengan memanggil SSLContext.getInstance("TLSv1.2"). Anda juga dapat mengaktifkan atau menonaktifkan versi protokol berbasis per koneksi dengan memanggil setEnabledProtocols() di objek yang sesuai.

Sertifikat yang ditandatangani dengan SHA-1 tidak dipercaya di TLS

Di Android 10, sertifikat yang menggunakan algoritma hash SHA-1 tidak dipercaya dalam koneksi TLS. CA root belum menerbitkan sertifikat seperti itu sejak 2016, dan sertifikat tersebut tidak lagi dipercaya di Chrome atau browser utama lainnya.

Setiap upaya untuk terhubung akan gagal jika koneksi dibuat ke situs yang menyajikan sertifikat yang menggunakan SHA-1.

Perubahan dan peningkatan perilaku KeyChain

Beberapa browser seperti Google Chrome memungkinkan pengguna untuk memilih sertifikat ketika server TLS mengirimkan pesan permintaan sertifikat sebagai bagian dari TLS handshake. Mulai Android 10, objek KeyChain mematuhi penerbit dan parameter spesifikasi utama saat memanggil KeyChain.choosePrivateKeyAlias() untuk menampilkan prompt pemilihan sertifikat kepada pengguna. Secara khusus, prompt ini tidak berisi pilihan yang tidak mematuhi spesifikasi server.

Jika tidak ada sertifikat yang bisa dipilih pengguna, seperti ketika tidak ada sertifikat yang cocok dengan spesifikasi server atau perangkat tidak memiliki sertifikat yang terinstal, prompt pemilihan sertifikat tidak akan muncul.

Selain itu, di Android 10 atau yang lebih tinggi, Anda tidak harus menyetel kunci layar perangkat untuk mengimpor kunci atau sertifikat CA ke objek KeyChain.

Perubahan TLS dan kriptografi lainnya

Ada beberapa perubahan kecil dalam library TLS dan kriptografi yang diberlakukan di Android 10:

  • Cipher AES/GCM/NoPadding dan ChaCha20/Poly1305/NoPadding menampilkan ukuran buffer yang lebih akurat dari getOutputSize().
  • Cipher suite TLS_FALLBACK_SCSV dihilangkan dari upaya koneksi dengan protokol maksimum TLS 1.2 atau yang lebih baru. Dengan diterapkannya peningkatan di server TLS, sebaiknya Anda tidak mencoba penggantian TLS eksternal. Sebagai gantinya, cobalah untuk mengandalkan negosiasi versi TLS.
  • ChaCha20-Poly1305 adalah alias untuk ChaCha20/Poly1305/NoPadding.
  • Nama host yang memiliki titik akhir tidak dianggap sebagai nama host SNI yang valid.
  • Ekstensi supported_signature_algorithms dalam CertificateRequest dipatuhi ketika memilih kunci penandatanganan untuk respons sertifikat.
  • Kunci penandatanganan dengan akses terbatas, seperti yang berasal dari Android Keystore, dapat digunakan dengan tanda tangan RSA-PSS di TLS.

Perubahan koneksi HTTPS

Jika aplikasi yang menjalankan Android 10 meneruskan null ke setSSLSocketFactory(), IllegalArgumentException akan terjadi. Pada versi sebelumnya, meneruskan null ke setSSLSocketFactory() memiliki efek yang sama dengan meneruskan nilai default pabrik yang aktif.

Android 11

Soket SSL menggunakan mesin SSL Conscrypt secara default

Implementasi SSLSocket default Android didasarkan pada Conscrypt. Mulai Android 11, implementasi tersebut dikembangkan secara internal di atas SSLEngine Conscrypt.