mTLS (Mutual Transport Layer Security)

Apa Itu mTLS?

Mutual TLS (mTLS), atau Transport Layer Security Bersama, adalah sebuah metode otentikasi dan enkripsi yang memastikan bahwa kedua belah pihak dalam sebuah komunikasi—baik klien maupun server—saling memverifikasi identitas satu sama lain. Ini merupakan peningkatan dari protokol TLS standar (yang mungkin lebih Anda kenal sebagai SSL) di mana umumnya hanya klien yang memverifikasi identitas server.

Mari kita bedah perbedaannya:

  • Dalam TLS Standar: Ketika Anda mengakses sebuah situs web seperti https://www.google.com, browser Anda (klien) akan meminta sertifikat TLS dari server Google. Browser kemudian akan memverifikasi bahwa sertifikat tersebut valid dan dikeluarkan oleh Otoritas Sertifikat (Certificate Authority/CA) yang terpercaya. Proses ini memastikan bahwa Anda benar-benar terhubung ke server Google yang asli, bukan server palsu. Namun, server Google tidak memverifikasi identitas Anda secara kriptografis; ia hanya tahu ada seseorang yang terhubung.

  • Dalam mTLS: Proses verifikasi terjadi dua arah. Tidak hanya klien yang memverifikasi server, tetapi server juga meminta dan memverifikasi sertifikat dari klien. Klien harus menunjukkan sertifikat yang valid yang dipercaya oleh server. Dengan demikian, server tidak hanya tahu bahwa koneksinya aman, tetapi juga tahu persis siapa yang terhubung. Ini menciptakan sebuah kanal komunikasi yang sangat aman karena hanya pihak-pihak dengan sertifikat yang telah disetujui sebelumnya yang dapat saling terhubung.

Singkatnya, jika TLS adalah seperti menunjukkan KTP Anda kepada satpam (server) untuk masuk ke sebuah gedung, maka mTLS adalah seperti satpam juga menunjukkan kartu identitasnya kepada Anda untuk membuktikan bahwa ia adalah satpam yang sah di gedung tersebut.


Praktik dan Penerapan mTLS

mTLS digunakan dalam skenario di mana keamanan dan otentikasi yang kuat dari kedua belah pihak sangatlah krusial. Ini sering kali diimplementasikan untuk komunikasi non-manusia, seperti antar layanan atau perangkat.

Berikut adalah beberapa praktik dan contoh penerapan lengkapnya:

1. Keamanan Microservices dan Komunikasi Antar-Layanan

Ini adalah kasus penggunaan mTLS yang paling umum saat ini, terutama dalam arsitektur berbasis service mesh seperti Istio atau Linkerd.

  • Masalah: Dalam arsitektur microservices, puluhan atau bahkan ratusan layanan kecil saling berkomunikasi satu sama lain untuk menjalankan sebuah aplikasi. Bagaimana layanan A bisa yakin bahwa ia benar-benar sedang berbicara dengan layanan B yang sah, dan bukan layanan palsu yang menyusup ke dalam jaringan?

  • Solusi mTLS: Setiap microservice diberikan sertifikat identitasnya sendiri. Ketika layanan A ingin berkomunikasi dengan layanan B, mereka akan melakukan mTLS handshake. Layanan A memverifikasi sertifikat layanan B, dan sebaliknya. Jika verifikasi berhasil, koneksi aman terjalin. Jika gagal (misalnya, ada layanan jahat mencoba terhubung tanpa sertifikat yang valid), koneksi akan langsung ditolak.

  • Praktik:

    • Setiap layanan memiliki sidecar proxy (seperti Envoy) yang menangani proses handshake mTLS secara otomatis.

    • Pengembang tidak perlu mengubah kode aplikasi mereka untuk mengimplementasikan mTLS; service mesh yang akan mengelolanya.

    • Ini memungkinkan kebijakan keamanan zero-trust (jangan percaya siapapun secara default), di mana setiap komunikasi harus diotentikasi dan dienkripsi, bahkan di dalam jaringan internal yang dianggap "aman".

2. Keamanan Perangkat Internet of Things (IoT)

Perangkat IoT, seperti sensor industri, kamera keamanan pintar, atau perangkat medis, sering kali mengirimkan data sensitif dan perlu dikelola dari jarak jauh.

  • Masalah: Bagaimana cara memastikan bahwa data dari sensor di lapangan benar-benar berasal dari sensor yang sah? Dan bagaimana cara server pusat mengirimkan perintah (misalnya, pembaruan firmware) hanya ke perangkat yang dituju, bukan ke perangkat palsu?

  • Solusi mTLS: Setiap perangkat IoT dipasangi sertifikat klien saat diproduksi. Ketika perangkat tersebut "bangun" dan ingin terhubung ke server pusat, ia akan menggunakan sertifikat tersebut untuk mengotentikasi dirinya melalui mTLS. Server akan memverifikasi sertifikat perangkat sebelum menerima data apa pun darinya. Sebaliknya, perangkat juga akan memverifikasi server untuk memastikan ia terhubung ke pusat kendali yang benar.

  • Praktik:

    • Sertifikat klien ditanamkan secara aman di dalam hardware perangkat.

    • Server memiliki daftar sertifikat atau CA yang dipercaya untuk perangkat yang diizinkan terhubung.

    • Jika sebuah perangkat dicuri atau disusupi, sertifikatnya dapat dicabut (revoked) di server, sehingga perangkat tersebut tidak bisa lagi terhubung ke jaringan.

3. API Security untuk Klien Bisnis (B2B)

Ketika sebuah perusahaan menyediakan API untuk digunakan oleh mitra bisnisnya.

  • Masalah: Sebuah perusahaan finansial menyediakan API untuk mitranya agar dapat melakukan transaksi. Mereka perlu memastikan bahwa hanya mitra bisnis yang sah yang dapat mengakses API tersebut, bukan peretas atau pihak tidak berwenang. Menggunakan kunci API (API Key) saja terkadang tidak cukup aman karena bisa bocor.

  • Solusi mTLS: Selain menggunakan kunci API, perusahaan tersebut mewajibkan setiap aplikasi mitra untuk menggunakan sertifikat klien saat mengakses API. Server API akan menolak koneksi apa pun yang tidak menyajikan sertifikat klien yang valid dan terdaftar.

  • Praktik:

    • Perusahaan akan menerbitkan sertifikat klien kepada setiap mitra bisnisnya.

    • Sertifikat ini memiliki masa berlaku dan dapat dicabut jika kerja sama bisnis berakhir.

    • Ini menambahkan lapisan keamanan yang sangat kuat di atas metode otentikasi lainnya seperti OAuth 2.0 atau kunci API.

4. Otentikasi Klien di Aplikasi Internal Perusahaan

Untuk aplikasi internal yang sangat sensitif, seperti portal admin atau dasbor keuangan.

  • Masalah: Bagaimana cara membatasi akses ke aplikasi internal yang sangat sensitif hanya untuk perangkat yang dimiliki dan dikelola oleh perusahaan? Mengandalkan username dan password saja rentan terhadap phishing.

  • Solusi mTLS: Perusahaan memasang sertifikat klien pada setiap laptop atau perangkat kerja karyawan yang sah. Ketika seorang karyawan mencoba mengakses portal admin, browser mereka akan secara otomatis menyajikan sertifikat tersebut ke server. Server akan memverifikasi sertifikat tersebut sebelum bahkan menampilkan halaman login. Jika seseorang mencoba mengakses dari perangkat pribadi mereka (yang tidak memiliki sertifikat), koneksi akan gagal.

  • Praktik:

    • Sertifikat didistribusikan ke perangkat perusahaan melalui Mobile Device Management (MDM) atau perangkat lunak manajemen lainnya.

    • Ini sering digabungkan dengan metode otentikasi lain (seperti Single Sign-On) untuk keamanan berlapis.


Kesimpulan

Secara ringkas, mTLS adalah standar emas untuk otentikasi dua arah yang memastikan tingkat kepercayaan dan keamanan tertinggi dalam komunikasi digital. Meskipun tidak umum digunakan untuk situs web publik biasa karena kompleksitas dalam mendistribusikan sertifikat ke jutaan pengguna, mTLS sangat penting dan menjadi praktik terbaik dalam lingkungan di mana identitas setiap pihak—bukan hanya server—harus diverifikasi tanpa keraguan, seperti dalam komunikasi antar microservices, perangkat IoT, dan API B2B.


Sekarang kuta akan langsung praktik (hands-on) untuk menyiapkan mTLS, baik untuk pengujian di lokal maupun untuk persiapan produksi. Kita akan membuat semua sertifikat yang diperlukan dari awal dan membangun aplikasi server & klien sederhana menggunakan Golang.

Langkah 1: Persiapan - Membuat Sertifikat

Ini adalah fondasi dari mTLS. Kita akan bertindak sebagai Certificate Authority (CA) kita sendiri untuk mengeluarkan sertifikat bagi server dan klien. Untuk ini, kita akan menggunakan openssl.

Buka terminal Anda dan buat direktori kerja baru.

mkdir go-mtls-example
cd go-mtls-example

Sekarang, mari buat semua kunci dan sertifikat yang diperlukan.

  1. Buat Kunci Privat dan Sertifikat untuk CA (Certificate Authority)

    • CA ini adalah entitas yang kita percayai untuk menandatangani (memverifikasi) sertifikat server dan klien.

    # Buat kunci privat untuk CA
    openssl genrsa -out ca.key 2048
    
    # Buat sertifikat root CA yang self-signed (berlaku 365 hari)
    openssl req -new -x509 -sha256 -days 365 -key ca.key -out ca.crt -subj "/CN=My Own CA"

    Anda sekarang memiliki ca.key (rahasia) dan ca.crt (publik).

  2. Buat Kunci Privat dan Sertifikat untuk Server

    • Ini akan digunakan oleh server kita untuk membuktikan identitasnya kepada klien.

    # Buat kunci privat untuk server
    openssl genrsa -out server.key 2048
    
    # Buat Certificate Signing Request (CSR) untuk server
    # CN=localhost penting untuk pengujian di mesin lokal
    openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=localhost"
    
    # Gunakan CA kita untuk menandatangani CSR server dan membuat sertifikatnya
    openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

    Anda sekarang memiliki server.key, server.csr (bisa dihapus), dan server.crt.

  3. Buat Kunci Privat dan Sertifikat untuk Klien

    • Ini akan digunakan oleh klien untuk membuktikan identitasnya kepada server.

    # Buat kunci privat untuk klien
    openssl genrsa -out client.key 2048
    
    # Buat CSR untuk klien
    openssl req -new -sha256 -key client.key -out client.csr -subj "/CN=my-client"
    
    # Gunakan CA kita untuk menandatangani CSR klien dan membuat sertifikatnya
    openssl x509 -req -sha256 -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

    Anda sekarang memiliki client.key, client.csr (bisa dihapus), dan client.crt.

Setelah langkah ini, direktori Anda seharusnya berisi file-file berikut:

ca.key, ca.crt, ca.srl, server.key, server.crt, client.key, client.crt


Langkah 2: Membangun Aplikasi Golang

Kita akan membuat dua file: server.go dan client.go.

server.go

Server ini akan melayani permintaan di port 8443 dan akan mewajibkan serta memverifikasi sertifikat dari setiap klien yang masuk.

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	// Cetak Common Name dari sertifikat klien yang terverifikasi
	clientCN := r.TLS.PeerCertificates[0].Subject.CommonName
	log.Printf("Received request from client: %s\n", clientCN)
	fmt.Fprintf(w, "Hello, you are authenticated as %s!\n", clientCN)
}

func main() {
	// Muat sertifikat server dan kunci privatnya
	serverCert, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		log.Fatalf("Failed to load server certificate and key: %s", err)
	}

	// Muat sertifikat CA yang akan digunakan untuk memverifikasi klien
	caCert, err := ioutil.ReadFile("ca.crt")
	if err != nil {
		log.Fatalf("Failed to read CA certificate: %s", err)
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	// Konfigurasi TLS untuk server
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{serverCert},
		ClientCAs:    caCertPool,
		// Wajibkan dan verifikasi sertifikat klien
		ClientAuth: tls.RequireAndVerifyClientCert,
	}

	// Buat server HTTP
	server := &http.Server{
		Addr:      ":8443",
		TLSConfig: tlsConfig,
	}

	http.HandleFunc("/", helloHandler)

	log.Println("Starting mTLS server on :8443")
	// Jalankan server dengan HTTPS (Listen and Serve TLS)
	if err := server.ListenAndServeTLS("", ""); err != nil {
		log.Fatalf("Failed to start server: %s", err)
	}
}

client.go

Klien ini akan membuat permintaan ke server sambil menyajikan sertifikatnya sendiri untuk otentikasi.

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	// Muat sertifikat klien dan kunci privatnya
	clientCert, err := tls.LoadX509KeyPair("client.crt", "client.key")
	if err != nil {
		log.Fatalf("Failed to load client certificate and key: %s", err)
	}

	// Muat sertifikat CA yang akan digunakan untuk memverifikasi server
	caCert, err := ioutil.ReadFile("ca.crt")
	if err != nil {
		log.Fatalf("Failed to read CA certificate: %s", err)
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	// Konfigurasi TLS untuk klien
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      caCertPool,
	}

	// Buat transport HTTP dengan konfigurasi mTLS
	transport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}
	client := &http.Client{
		Transport: transport,
	}

	// Buat permintaan ke server
	resp, err := client.Get("https://localhost:8443")
	if err != nil {
		log.Fatalf("Failed to make request: %s", err)
	}
	defer resp.Body.Close()

	// Baca dan cetak respons dari server
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Failed to read response body: %s", err)
	}

	fmt.Printf("Status Code: %d\n", resp.StatusCode)
	fmt.Printf("Response Body: %s\n", string(body))
}

Langkah 3: Menjalankan dan Menguji di Lokal

Sekarang saatnya melihat hasilnya!

  1. Jalankan Server:

    Buka terminal pertama dan jalankan server.go.

    go run server.go

    Outputnya akan:

    2025/09/28 12:15:52 Starting mTLS server on :8443
  2. Jalankan Klien:

    Buka terminal kedua dan jalankan client.go.

    go run client.go

    Outputnya akan:

    Status Code: 200
    Response Body: Hello, you are authenticated as my-client!

    Kembali ke terminal server, Anda akan melihat log ini, membuktikan server mengenali klien:

    2025/09/28 12:16:00 Received request from client: my-client

    Sukses! Anda baru saja melakukan komunikasi mTLS.

  3. Uji Kegagalan (Penting!):

    Bagaimana jika ada klien yang mencoba terhubung tanpa sertifikat yang valid? Mari kita coba gunakan curl.

    • Percobaan 1: curl biasa (tanpa sertifikat)

      # Opsi -k atau --insecure diperlukan karena server kita menggunakan CA self-signed
      curl -k https://localhost:8443

      Outputnya akan berupa error, seperti curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate. Server menolak koneksi karena klien tidak menyediakan sertifikat. ❌

    • Percobaan 2: curl dengan sertifikat klien

      curl --key client.key --cert client.crt --cacert ca.crt https://localhost:8443

      Outputnya akan sukses:

      Hello, you are authenticated as my-client!

      Ini membuktikan bahwa hanya klien yang memiliki sertifikat yang ditandatangani oleh CA kita yang dapat terhubung.


Langkah 4: Pertimbangan untuk Produksi

Setup di atas sempurna untuk pengembangan lokal. Untuk produksi, pendekatannya perlu lebih matang, terutama dalam manajemen sertifikat dan infrastruktur.

  1. Manajemen Sertifikat:

    • Jangan Gunakan openssl Manual: Di produksi, Anda memerlukan sistem CA yang lebih kuat dan otomatis. Pilihan populer meliputi:

      • HashiCorp Vault: Solusi lengkap untuk manajemen rahasia dan dapat berfungsi sebagai CA internal yang kuat untuk menerbitkan sertifikat secara dinamis.

      • CFSSL (CloudFlare's PKI/TLS toolkit): Alat yang bagus untuk membangun infrastruktur PKI internal.

      • AWS Certificate Manager Private CA / Google Cloud Certificate Authority Service: Layanan cloud terkelola untuk membuat dan mengelola CA pribadi Anda.

    • Sertifikat Berumur Pendek (Short-lived Certificates): Alih-alih sertifikat yang berlaku selama setahun, praktik modern menyarankan sertifikat yang hanya berlaku beberapa jam atau hari. Ini mengurangi risiko jika sebuah kunci privat bocor. Sistem seperti Vault dan service mesh dapat mengotomatiskan rotasi sertifikat ini.

  2. Pemisahan Tanggung Jawab (Separation of Concerns):

    • Terminasi mTLS di Edge: Seringkali, aplikasi Go itu sendiri tidak menangani terminasi TLS/mTLS. Beban kerja kriptografis ini dialihkan ke lapisan infrastruktur, seperti:

      • Reverse Proxy: NGINX, HAProxy, atau Caddy dapat dikonfigurasi di depan aplikasi Anda untuk menangani mTLS. Mereka akan memverifikasi sertifikat klien dan kemudian meneruskan permintaan ke aplikasi Anda (seringkali melalui HTTP biasa di jaringan internal), mungkin dengan menambahkan header (misalnya, X-Client-CN) yang berisi identitas klien yang telah diverifikasi.

      • Service Mesh (Istio, Linkerd): Ini adalah pendekatan paling umum untuk mTLS dalam arsitektur microservices. Sebuah sidecar proxy (seperti Envoy) berjalan di samping setiap layanan aplikasi Anda. Sidecar ini secara otomatis menangani semua komunikasi mTLS (enkripsi, dekripsi, verifikasi, rotasi sertifikat) secara transparan. Pengembang aplikasi Anda bahkan tidak perlu menulis kode TLS sama sekali!

  3. Distribusi dan Penyimpanan Rahasia:

    • Kunci privat (.key) adalah rahasia yang sangat sensitif. Jangan pernah menyimpannya langsung di dalam kode atau container image.

    • Gunakan sistem manajemen rahasia seperti Kubernetes Secrets, AWS Secrets Manager, Google Secret Manager, atau HashiCorp Vault untuk menyimpan kunci privat dengan aman dan menyediakannya ke aplikasi Anda saat runtime.


Jika anda menjalankan client.go dan mendapati error berikut:

ailed to make request: Get "https://localhost:8443": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead

ini terjadi karena versi Go yang lebih baru (dimulai dari Go 1.15) sudah tidak lagi menggunakan Common Name (CN) pada sertifikat untuk verifikasi nama host. Praktik keamanan modern mewajibkan nama host (seperti localhost) untuk dicantumkan di dalam ekstensi Subject Alternative Name (SAN) dari sertifikat.

Error x509: certificate relies on legacy Common Name field, use SANs instead secara harfiah berarti "sertifikat ini masih menggunakan field Common Name yang lama, harap gunakan SAN".

Ini adalah error yang sangat umum terjadi dan perbaikannya cukup mudah. Kita hanya perlu menambahkan informasi SAN saat membuat sertifikat server.


Solusi: Perbarui Perintah Pembuatan Sertifikat Server

Anda perlu menghapus sertifikat server lama dan membuatnya kembali dengan perintah yang menyertakan konfigurasi SAN.

Langkah 1: Buat File Konfigurasi untuk SAN

Di direktori go-mtls-example Anda, buat file baru bernama server_ext.cnf dan isi dengan konten berikut:

server_ext.cnf

Ini, TOML

[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
  • File ini memberitahu openssl untuk menambahkan ekstensi subjectAltName ke sertifikat.

  • DNS.1 = localhost secara eksplisit menyatakan bahwa sertifikat ini valid untuk nama domain localhost.


Langkah 2: Hapus Sertifikat Lama dan Buat yang Baru

  1. Hapus file sertifikat server lama dan file serial CA agar tidak terjadi konflik.

    Bash

    rm server.crt ca.srl
  2. Jalankan kembali perintah untuk menandatangani sertifikat server, tetapi kali ini dengan menambahkan flag -extfile untuk menggunakan file konfigurasi yang baru saja kita buat.

    Perintah lama: openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

    Perintah baru yang sudah diperbaiki:

    Bash

    openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile server_ext.cnf -extensions v3_req

Ringkasan Perintah Lengkap (Untuk Dijalankan Ulang)

Berikut adalah urutan perintah lengkap dari awal dengan perbaikan yang sudah disertakan. Anda bisa menjalankan ini di direktori yang bersih untuk memastikan semuanya benar.

# 1. Buat Kunci dan Sertifikat CA (Tidak berubah)
openssl genrsa -out ca.key 2048
openssl req -new -x509 -sha256 -days 365 -key ca.key -out ca.crt -subj "/CN=My Own CA"

# 2. Buat Kunci dan CSR Server (Tidak berubah)
openssl genrsa -out server.key 2048
openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=localhost"

# 3. Buat file konfigurasi SAN (Langkah Baru)
cat <<EOF > server_ext.cnf
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
EOF

# 4. Tandatangani Sertifikat Server DENGAN SAN (Langkah yang Diperbarui)
openssl x509 -req -sha256 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile server_ext.cnf -extensions v3_req

# 5. Buat Kunci dan Sertifikat Klien (Tidak berubah)
openssl genrsa -out client.key 2048
openssl req -new -sha256 -key client.key -out client.csr -subj "/CN=my-client"
openssl x509 -req -sha256 -days 365 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

Setelah Anda membuat ulang sertifikat server dengan benar menggunakan perintah di atas, jalankan kembali aplikasi Anda.

  1. Terminal 1: go run server.go

  2. Terminal 2: go run client.go

Sekarang klien Anda akan berjalan dengan sukses tanpa error tersebut.

Last updated