Setiap request HTTP yang kita kirim ke API pembayaran atau layanan cloud melintasi jaringan yang tidak kita kendalikan: router kantor, ISP, backbone, dan load balancer milik orang lain. Tanpa enkripsi, siapa pun di jalur tersebut bisa membaca dan bahkan mengubah isi request. Protokol TLS menutup celah itu, dan satu kesalahan konfigurasi di sisi klien — menonaktifkan verifikasi — mengembalikan seluruh risikonya. Chapter ini merangkum apa yang dijamin TLS dan bagaimana menggunakannya dengan benar dari Python.
Apa yang Dijamin TLS #
TLS memberi tiga jaminan sekaligus dalam satu koneksi:
| Jaminan | Yang dicegah | Mekanisme |
|---|---|---|
| Enkripsi | Penyadap membaca isi request dan response | Kunci sesi yang dinegosiasikan secara asimetris |
| Identitas | Penyerang menyamar sebagai server tujuan | Sertifikat digital yang diterbitkan certificate authority |
| Integritas | Isi paket dimodifikasi di tengah jalan | Message authentication code pada setiap record |
Sertifikat adalah jaminan yang paling sering disalahpahami: URL https:// saja belum berarti aman bila kode kita tidak memverifikasinya. Sertifikat mengikat sebuah nama domain ke kunci publik, dan verifikasinya memeriksa rantai penerbit hingga akar yang dipercaya — plus masa berlaku dan kecocokan hostname.
requests Sudah Memverifikasi secara Default #
Library requests memverifikasi sertifikat secara bawaan. Kita buktikan dengan mengunjungi server uji self-signed.badssl.com yang memakai sertifikat tanpa penerbit tepercaya:
import requests
try:
requests.get("https://example.com", timeout=10)
print("example.com: koneksi terenkripsi terverifikasi")
except requests.exceptions.SSLError as e:
print("example.com: SERTIFIKAT DITOLAK ->", e)
try:
requests.get("https://self-signed.badssl.com/", timeout=10)
print("self-signed.badssl.com: koneksi berhasil (seharusnya gagal!)")
except requests.exceptions.SSLError as e:
print("self-signed.badssl.com DITOLAK oleh requests:")
print(e)Output yang Dihasilkan:
example.com: koneksi terenkripsi terverifikasi
self-signed.badssl.com DITOLAK oleh requests:
HTTPSConnectionPool(host='self-signed.badssl.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1010)')))Mari kita bedah kejadiannya:
- Baris 4-5 (
requests.get("https://example.com", timeout=10)): request berhasil karena sertifikatexample.comberantai ke certificate authority yang dikenali oleh kumpulan sertifikat tepercaya sistem operasi. - Baris 9-14 (
self-signed.badssl.com): sertifikat situs ini ditandatangani sendiri tanpa penerbit, sehinggarequestsmemunculkanSSLErrorsebelum satu byte pun terkirim — koneksi ditolak lebih awal, bukan dienkripsi sambil mengabaikan identitas. - Pesan error (
CERTIFICATE_VERIFY_FAILED): teks ini menandakan verifikasi bekerja normal. Menemukannya di log produksi berarti server tujuan bermasalah, bukan library kita.
Perlu dicatat juga sertifikat pada mesin pembaca: Python versi tertentu (terutama dari python.org di macOS dan Windows) membawa kumpulan sertifikatnya sendiri melalui package certifi — pip install --upgrade certifi adalah perbaikan pertama saat muncul galat verifikasi yang tidak semestinya.
Anti-Pattern: verify=False #
Saat menghadapi server internal bersertifikat self-signed, banyak pengembang memilih jalan pintas:
import requests
# ANTI-PATTERN: jangan ditiru
requests.get("https://server-internal.example.com", verify=False)Solusi yang benar untuk server internal adalah mempercayai certificate authority internal organisasi: sertifikat CA-nya didaftarkan ke sistem operasi, atau file tersebut diteruskan lewat requests.get(url, verify="/path/ca-bundle.crt").
Module ssl dan Konteks Produksi #
Untuk koneksi tingkat rendah — socket sendiri atau klien kustom — standard library menyediakan module ssl. Pintu masuk yang benar adalah ssl.create_default_context(), yang menyiapkan pengaturan aman bawaan:
import ssl
context = ssl.create_default_context()
print("Verifikasi sertifikat aktif? ", context.verify_mode == ssl.CERT_REQUIRED)
print("Pemeriksaan hostname aktif? ", context.check_hostname)Output yang Dihasilkan:
Verifikasi sertifikat aktif? True
Pemeriksaan hostname aktif? TrueContext hasil create_default_context() memverifikasi sertifikat (CERT_REQUIRED) dan mencocokkan hostname tujuan — dua pemeriksaan yang harus tetap menyala. Konteks ini bisa diteruskan ke socket_terbungkus = context.wrap_socket(sock, server_hostname=host) saat membungkus socket biasa menjadi koneksi TLS.
Dua catatan penutup. Certificate pinning — membandingkan sidik jari sertifikat server dengan nilai yang dikunci di kode — hanya untuk kasus khusus seperti aplikasi perbankan berisiko tinggi, karena pembaruan sertifikat server akan memutus klien kita. Dan layanan internal antar server sekalipun sebaiknya berjalan di atas TLS: jaringan privat tidak menutup kemungkinan penyusup bergerak menyamping setelah menembus satu titik. Dengan TLS terverifikasi di semua jalur, sisa pekerjaan kita adalah membuktikan bahwa seluruh pertahanan ini benar-benar bekerja — lewat test pada Chapter Security Testing.