Skip to content
Kembali ke Blog
Keamanan

Cara Kerja TOTP: Algoritma di Balik Kode 2FA

Cara kerja TOTP untuk developer: algoritma RFC 6238 langkah demi langkah, cara server memverifikasi kode, dan apa yang sebenarnya dilindungi 2FA.

12 menit membaca

Cara Kerja TOTP: Algoritma di Balik Kode Authenticator Anda

Anda mengetuk kode 6 digit dari aplikasi authenticator beberapa kali seminggu, dan entah bagaimana kode yang sama muncul di ponsel Anda dan cocok dengan yang diharapkan server, padahal keduanya tidak pernah saling berkomunikasi. Jadi, bagaimana cara kerja TOTP? Shared secret yang sama menghasilkan angka baru setiap 30 detik lewat sebuah algoritma deterministik kecil yang dijalankan kedua sisi secara terpisah. Tidak ada kode yang melintasi jaringan, dan tidak ada server pusat yang membagikan angka itu.

TOTP (Time-based One-Time Password), yang didefinisikan dalam RFC 6238, mengubah sebuah shared secret ditambah waktu saat ini menjadi kode numerik pendek dengan menjalankan HMAC atas waktu lalu memotong (truncate) hasilnya. Autentikasi dua faktor (two-factor authentication, 2FA) bergantung pada kedua sisi yang menghitung nilai yang sama tanpa saling menukarnya, jadi algoritma ini memikul seluruh model kepercayaannya.

Panduan ini menelusuri algoritma dari ujung ke ujung dengan angka konkret, lalu membahas bagian yang dilewatkan oleh kebanyakan penjelasan: bagaimana server sebenarnya memverifikasi sebuah kode, dan penjelasan jujur tentang apa yang dicegah 2FA dan apa yang tidak. Anda bisa menghitung kode secara langsung di generator TOTP kami sambil membaca.

Apa itu TOTP, sebenarnya?

TOTP (Time-based One-Time Password), yang didefinisikan dalam RFC 6238, adalah algoritma yang menggabungkan sebuah shared secret dengan waktu saat ini untuk menghasilkan kode pendek yang berputar pada interval tetap. Baik aplikasi authenticator maupun server menyimpan secret yang sama, membaca jam yang sama, dan menjalankan perhitungan yang sama, sehingga keduanya sampai pada kode yang sama tanpa pernah mengirimkannya.

Bagian terakhir itu penting. Kode tersebut tidak pernah dikirim saat penyiapan; yang dikirim hanya secret-nya, dan setelah itu masing-masing sisi menurunkan kode sendiri. Di jalur jaringan tidak ada yang bisa disadap kecuali secret saat pendaftaran dan 6 digit yang diketik pengguna saat login. Anggap saja tiga input menghasilkan satu output:

InputPeranNilai umum
Shared secretKunci berumur panjang, disepakati sekali saat pendaftaranJBSWY3DPEHPK3PXP (Base32)
Time stepPenghitung yang berdetak majuJendela 30 detik
OutputKode pendek yang diturunkan dari keduanya324550

Secret hampir selalu ditulis dalam Base32 (huruf A–Z dan digit 2–7) karena alfabet itu tidak peka huruf besar/kecil dan tetap utuh saat dicetak, diketik, atau dikemas ke dalam kode QR. Anda mendaftarkan sebuah secret dengan memindai URI otpauth://, yang bisa Anda render sebagai QR authenticator, atau dengan mengetik string Base32 secara manual.

TOTP vs HOTP vs SMS vs Passkey

TOTP hanyalah satu opsi di antara beberapa, dan untuk memilih dengan tepat Anda perlu tahu bedanya. Hubungan yang paling penting: TOTP adalah HOTP dengan penghitung yang diganti oleh jumlah time step sejak Unix epoch. Sisanya adalah pertukaran (trade-off) antara ketahanan terhadap phishing, kenyamanan, dan infrastruktur yang Anda butuhkan.

MekanismePemicuMasa pakai kodeTahan phishing?Butuh jaringan?Penggunaan umum
HOTP (RFC 4226)Penghitung yang naikHingga dipakaiTidakTidakToken perangkat keras, legacy
TOTP (RFC 6238)Waktu saat ini~30 detikTidakTidak (setelah pendaftaran)Aplikasi authenticator
SMS OTPServer mengirim kodeBeberapa menitTidakYa (seluler)Cadangan konsumen
Push approvalServer meminta ke perangkatPer permintaanSebagianYa2FA berbasis aplikasi
Passkey / FIDO2Tantangan kunci publikPer permintaanYa (terikat origin)YaAkun modern

Perhatikan polanya. TOTP dan HOTP berjalan offline setelah didaftarkan, sehingga tangguh dan privat, tetapi keduanya tidak tahan phishing dengan sendirinya: halaman palsu yang meyakinkan bisa meminta kode lalu meneruskannya. SMS menambahkan kanal jaringan, yang memunculkan permukaan serangannya sendiri. Passkey menutup celah phishing dengan mengikat kredensial ke origin situs, dan ke sanalah industri bergerak. TOTP sendiri cukup kuat, ada di mana-mana, dan gratis, dan itu sebabnya ia masih begitu umum.

Cara kerja algoritma TOTP, langkah demi langkah

Berikut keseluruhan algoritma dalam empat langkah. Kita akan menjalankan masing-masing dengan secret uji dari RFC JBSWY3DPEHPK3PXP dan waktu Unix tetap 1700000000, sehingga setiap angka dapat direproduksi.

  1. Dekode secret Base32 menjadi byte kunci mentah.
  2. Hitung penghitung time-step dari waktu Unix saat ini.
  3. HMAC penghitung itu dengan kunci secret.
  4. Truncate digest menjadi kode 6 digit.

Langkah 1 — Dekode secret Base32 menjadi byte

Base32 mengemas 5 bit ke dalam setiap karakter. Dekoder mengelompokkan kembali karakter-karakter itu menjadi byte 8-bit. Secret JBSWY3DPEHPK3PXP terdekode menjadi 10 byte mentah 48 65 6c 6c 6f 21 de ad be ef. Array byte inilah, bukan string yang dapat dicetak, yang menjadi kunci HMAC.

Langkah 2 — Hitung penghitung time-step

Penghitung adalah jumlah time step utuh yang telah berlalu sejak titik awal: T = floor((unixTime − T0) / period). Nilai default RFC adalah T0 = 0 (Unix epoch) dan period = 30. Dengan unixTime = 1700000000, hasilnya T = floor(1700000000 / 30) = 56666666. Bilangan bulat ini kemudian dikodekan sebagai nilai big-endian 8 byte: 00 00 00 00 03 60 aa 2a. Karena penghitung hanya berubah saat jendela 30 detik baru dimulai, setiap kode stabil selama satu jendela lalu melompat.

Langkah 3 — HMAC penghitung dengan secret

Algoritma menjalankan HMAC-SHA1 atas penghitung 8 byte menggunakan byte secret sebagai kunci. HMAC adalah fungsi satu arah berkunci: tanpa secret Anda tidak bisa membalik digest atau memalsukan yang valid, dan itulah yang membuat kode tidak dapat dipalsukan. Untuk input kita, digest-nya adalah 20 byte 1d 70 6e 94 1a c7 6b 6d 4a 46 dd 6f af a4 5f e3 35 11 bf 86.

Langkah 4 — Truncation dinamis menjadi kode 6 digit (RFC 4226)

Digest 20 byte terlalu panjang untuk diketik, jadi truncation dinamis RFC 4226 mengekstrak sebuah angka darinya. Ambil nibble bawah dari byte terakhir sebagai offset: byte terakhir adalah 0x86, nibble bawahnya adalah 6, jadi offset-nya 6. Baca 4 byte mulai dari offset itu (6b 6d 4a 46), masking bit teratas dari byte pertama agar angkanya tetap positif, dan Anda mendapatkan bilangan bulat 1802324550. Kurangi modulo 10^6 lalu beri padding nol: 1802324550 % 1000000 = 324550. Itulah kode yang ditampilkan aplikasi Anda untuk secret ini pada saat ini.

Berikut algoritmanya dalam JavaScript menggunakan Web Crypto API bawaan browser, tanpa dependensi. Setiap komentar memetakan satu blok ke salah satu dari empat langkah di atas:

// TOTP per RFC 6238 — SHA-1, 6 digit, periode 30s (nilai default).
async function generateTotp(base32Secret, unixTime = Date.now() / 1000) {
  // Langkah 1: dekode secret Base32 (A-Z, 2-7) menjadi byte kunci mentah.
  const alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  let bits = '';
  for (const ch of base32Secret.replace(/=+$/, '').toUpperCase()) {
    bits += alpha.indexOf(ch).toString(2).padStart(5, '0');
  }
  const keyBytes = new Uint8Array(
    bits.match(/.{8}/g).map((b) => parseInt(b, 2)));

  // Langkah 2: counter = jumlah step 30s sejak epoch (big-endian 8 byte).
  let counter = Math.floor(unixTime / 30);
  const msg = new Uint8Array(8);
  for (let i = 7; i >= 0; i--) {
    msg[i] = counter & 0xff;
    counter = Math.floor(counter / 256);
  }

  // Langkah 3: HMAC-SHA1 penghitung dengan kunci secret.
  const key = await crypto.subtle.importKey(
    'raw', keyBytes, { name: 'HMAC', hash: 'SHA-1' }, false, ['sign']);
  const hmac = new Uint8Array(await crypto.subtle.sign('HMAC', key, msg));

  // Langkah 4: truncation dinamis (RFC 4226) -> kode 6 digit.
  const offset = hmac[hmac.length - 1] & 0x0f;
  const binary = ((hmac[offset] & 0x7f) << 24) | (hmac[offset + 1] << 16) |
                 (hmac[offset + 2] << 8) | hmac[offset + 3];
  return (binary % 1_000_000).toString().padStart(6, '0');
}

const code = await generateTotp('JBSWY3DPEHPK3PXP', 1700000000);
console.log(code); // -> "324550"

Algoritma yang sama dalam Python, hanya menggunakan pustaka standar (hmac dan struct):

import base64, hmac, hashlib, struct, time

def totp(secret, for_time=None, period=30, digits=6, digest='sha1'):
    if for_time is None:
        for_time = time.time()
    # Langkah 1: Base32-decode secret menjadi byte kunci mentah.
    key = base64.b32decode(secret.upper())
    # Langkah 2: counter = jumlah time step sejak epoch (big-endian 8 byte).
    counter = int(for_time // period)
    msg = struct.pack(">Q", counter)
    # Langkah 3: HMAC penghitung dengan secret.
    h = hmac.new(key, msg, digest).digest()
    # Langkah 4: truncation dinamis (RFC 4226) -> kode N digit.
    offset = h[-1] & 0x0f
    binary = ((h[offset] & 0x7f) << 24 |
              (h[offset + 1] & 0xff) << 16 |
              (h[offset + 2] & 0xff) << 8 |
              (h[offset + 3] & 0xff))
    return str(binary % (10 ** digits)).zfill(digits)

print(totp('JBSWY3DPEHPK3PXP', 1700000000))  # -> 324550

Kedua implementasi mencetak 324550 untuk waktu tetap kita, dan keduanya mereproduksi test vector resmi RFC 6238 (misalnya, vektor SHA-1 pada T = 59 menghasilkan 94287082). Jika Anda mengganti SHA-1 dengan SHA-256 atau SHA-512, atau mengubah jumlah digit, verifier di sisi lain harus mencocokkan pilihan yang sama persis atau kode tidak akan pernah cocok.

Memverifikasi kode TOTP di sisi server

Menghasilkan kode hanyalah separuh sistem. Separuh lainnya adalah server yang memutuskan apakah akan menerima 6 digit yang baru saja diketik pengguna, dan langkah itu membawa semua trade-off yang sensitif terhadap keamanan.

Server tidak menyimpan kode. Ia menyimpan secret, dan saat login ia menghitung ulang kode yang diharapkan dari secret itu dan waktu saat ini, lalu membandingkannya. Tantangannya adalah pergeseran jam (clock drift): perangkat pengguna dan server jarang sepakat pada detik yang persis, jadi pemeriksaan kesetaraan yang ketat akan menolak kode di dekat batas jendela. Solusinya adalah jendela validasi kecil. Terima step saat ini dan satu step di kedua sisi, yang berarti memeriksa kode untuk penghitung T−1, T, dan T+1. Jendela yang lebih lebar menoleransi lebih banyak drift tetapi memperbesar permukaan tebakan, jadi window 1 (toleransi ±30 detik) adalah keseimbangan yang umum. Toleransi ±1 step inilah yang bisa Anda amati di tab Verify pada generator TOTP kami.

import { createHmac, timingSafeEqual } from 'crypto';

function verifyTotp(secret, code, { window = 1, period = 30, digits = 6 } = {}) {
  const counter = Math.floor(Date.now() / 1000 / period);
  const submitted = Buffer.from(code);
  // Periksa step saat ini dan ±window step untuk pergeseran jam.
  for (let i = -window; i <= window; i++) {
    const expected = Buffer.from(totpAt(secret, counter + i, digits));
    // Perbandingan waktu-konstan agar timing tidak membocorkan kecocokan parsial.
    if (expected.length === submitted.length &&
        timingSafeEqual(expected, submitted)) {
      return counter + i; // step yang cocok — simpan untuk memblokir replay
    }
  }
  return false;
}

Dua detail lagi mengubah ini dari “berfungsi” menjadi “aman.” Pertama, pencegahan replay: simpan penghitung terakhir yang Anda terima untuk setiap pengguna dan tolak kode apa pun dari step yang sama atau lebih rendah, sehingga kode yang disadap sekali tidak bisa dipakai ulang dalam jendela yang sama. Itulah mengapa verifyTotp mengembalikan step yang cocok alih-alih sekadar true. Kedua, pembatasan laju (rate limiting): kode 6 digit adalah salah satu dari sejuta nilai, dan jendela ±1 membuat tiga di antaranya valid pada saat tertentu, jadi tanpa pembatasan seorang penyerang bisa melakukan brute-force atas ruang itu. Kunci akun atau tambahkan backoff setelah beberapa kali kegagalan. Terakhir, secret adalah kunci berumur panjang, jadi enkripsi saat disimpan (at rest), jauhkan dari source control, dan perlakukan persis seperti kata sandi. Buat kode pemulihan yang kuat di sampingnya untuk hari ketika sebuah perangkat hilang.

Apa yang dilindungi TOTP, dan apa yang tidak

TOTP adalah peningkatan nyata dibandingkan kata sandi saja, tetapi ia bukan obat mujarab, dan halaman pemasaran cenderung mengaburkan celah-celahnya.

TOTP mencegahTOTP TIDAK mencegah
Kata sandi bocor atau dipakai ulangPhishing real-time / adversary-in-the-middle
Credential stuffingMalware yang membaca secret dari perangkat
Brute force kata sandi jarak jauhAlur pemulihan akun lemah yang melewati 2FA
Pelanggaran DB yang hanya mengekspos hash kata sandi(ini membutuhkan pertahanan lain)

Manfaatnya nyata. Karena login kini membutuhkan kode yang hanya bisa dihasilkan oleh secret, kata sandi yang bocor tidak lagi cukup, dan itu langsung mematikan credential stuffing serta brute force jarak jauh. Jika basis data Anda bocor tetapi secret TOTP terenkripsi saat disimpan, penyerang tetap tidak bisa mencetak kode.

Celahnya juga sama nyatanya. Sebuah proxy phishing real-time (halaman adversary-in-the-middle) bisa menunjukkan replika sempurna kepada pengguna, menangkap kode langsung, lalu meneruskannya ke situs asli dalam jendela yang sama. TOTP tidak bisa membedakan bahwa kode itu diketik di tempat yang salah. Malware pada perangkat yang mengeksfiltrasi secret menggagalkannya sepenuhnya, dan alur pemulihan “lupa 2FA saya” yang ceroboh bisa mengakalinya sama sekali. Satu kekeliruan umum yang perlu diluruskan: serangan SIM-swap menggagalkan kode sekali pakai SMS, bukan TOTP. TOTP tidak punya kanal nomor telepon, jadi tidak ada yang bisa dialihkan oleh penyerang.

Langkah berikutnya biasanya passkey. Passkey dan FIDO2/WebAuthn terikat origin, sehingga tahan phishing secara desain: kredensialnya menolak untuk mengautentikasi ke domain yang salah. Perlakukan TOTP sebagai peningkatan yang kuat dan tersedia di mana-mana di atas kata sandi, bukan tujuan akhir. Ia cocok dipasangkan dengan sisa tumpukan autentikasi Anda: lihat praktik terbaik keamanan JWT untuk lapisan token sesi yang berjalan di atas login terverifikasi, dan hashing kata sandi (bcrypt vs Argon2) untuk lapisan kata-sandi-saat-disimpan yang dilengkapi oleh 2FA.

Jebakan umum saat mengimplementasikan TOTP

Sebagian besar bug TOTP bukan pada algoritmanya, yang sudah dipatok oleh RFC, melainkan pada perkawatan (wiring) di sekitarnya. Inilah yang paling sering menjebak para implementer.

  • Pergeseran jam server. Jika server tidak menjalankan NTP, gagasannya tentang “sekarang” akan menyimpang dari perangkat pengguna dan kode berhenti cocok untuk semua orang. Aktifkan sinkronisasi waktu jaringan di setiap host.
  • Secret berformat teks atau ter-commit. Sebuah secret di file konfigurasi yang masuk ke git adalah pintu belakang permanen. Simpan terenkripsi dalam secrets manager, jangan pernah di source control.
  • Tanpa proteksi replay. Jika Anda menerima kode tanpa mencatat step yang cocok, kode yang sama akan berfungsi lagi dalam jendelanya. Persistensikan step terakhir yang dipakai per pengguna dan tolak pemakaian ulang.
  • Jendela yang terlalu lebar atau terlalu sempit. Terlalu lebar menggandakan kode yang dapat ditebak dan melemahkan keamanan; terlalu sempit menolak pengguna sah pada drift kecil. Window 1 adalah keseimbangan yang biasa.
  • Ketidakcocokan parameter. Jika pendaftaran mengkodekan SHA-256 dan 8 digit dalam URI otpauth:// tetapi verifier mengasumsikan SHA-1 dan 6 digit, tidak ada kode yang akan pernah valid. Baca algoritma, jumlah digit, dan periode dari URI lalu gunakan keduanya di kedua sisi.
  • Tanpa kode cadangan atau pemulihan. Saat sebuah ponsel hilang, satu-satunya jalan masuk kembali adalah jalur pemulihan. Terbitkan kode pemulihan saat penyiapan, dan buat sekuat yang dituntut akun. Logika yang sama di balik entropi kata sandi berlaku juga untuk secret pemulihan.

FAQ

Apakah TOTP kebal phishing?

Tidak. TOTP mencegah kata sandi bocor dan brute force jarak jauh, tetapi proxy phishing real-time bisa menampilkan login palsu, menangkap kode langsung, dan meneruskannya ke situs asli dalam jendela 30 detik yang sama. Passkey dan FIDO2 adalah peningkatan yang tahan phishing karena mengikat kredensial ke origin situs.

Apakah TOTP lebih aman daripada 2FA SMS?

Ya. Kode SMS melintasi jaringan seluler dan bisa disadap lewat serangan SIM-swap atau SS7, dan bergantung pada keamanan operator Anda. TOTP tidak punya kanal nomor telepon dan tidak pernah mengirimkan kode sama sekali, jadi tidak ada yang bisa disadap saat transit. Secret ditukar sekali, saat penyiapan.

Apa yang terjadi jika saya kehilangan ponsel atau aplikasi authenticator?

Anda membutuhkan cadangan yang disiapkan sebelumnya. Pilihannya adalah kode pemulihan yang disimpan saat Anda menyiapkan 2FA, perangkat kedua yang didaftarkan dengan secret yang sama, atau secret Base32 asli yang disimpan di tempat aman. Tanpa salah satu dari ini, kehilangan perangkat berarti Anda terkunci dari akun.

Bagaimana server memverifikasi kode TOTP?

Ia menghitung ulang kode yang diharapkan dari shared secret dan waktu saat ini, lalu memeriksa kode yang dikirim terhadap time step saat ini dan satu step di kedua sisi untuk memberi kelonggaran pada pergeseran jam. Ia juga mencatat step mana yang cocok agar kode yang sama tidak bisa di-replay, dan membatasi laju percobaan untuk memblokir penebakan.

Mengapa kode TOTP diperbarui setiap 30 detik?

Tiga puluh detik adalah periode default RFC 6238: cukup panjang untuk membaca dan mengetik kode dengan nyaman, cukup pendek sehingga kode yang ditangkap penyerang kedaluwarsa hampir seketika. Beberapa sistem menggunakan periode 60 detik, yang dicatat oleh URI otpauth:// agar verifier mencocokkannya.

Bisakah dua perangkat berbagi satu secret TOTP?

Ya. Perangkat mana pun yang menyimpan secret Base32 yang sama dengan jam tersinkron menghasilkan kode yang identik, karena algoritmanya deterministik. Persis begitulah cara kerja cadangan authenticator multi-perangkat, dan itu juga sebabnya secret harus tetap privat: siapa pun yang menyalinnya bisa menghasilkan setiap kode di masa depan.

Apakah TOTP sama dengan Google Authenticator?

Tidak. TOTP adalah algoritma terbuka yang didefinisikan dalam RFC 6238. Google Authenticator, Authy, dan 1Password adalah aplikasi yang mengimplementasikannya. Karena standarnya bersama, aplikasi yang patuh mana pun bekerja dengan layanan mana pun yang menggunakan TOTP, jadi tidak ada penguncian (lock-in) ke vendor tertentu.

Kesimpulan

Inti idenya cukup ringkas untuk diingat:

  • TOTP mengubah sebuah shared secret ditambah waktu saat ini menjadi kode lewat HMAC dan truncation.
  • Kedua sisi menghitung kode secara independen; ia tidak pernah dikirim melalui jaringan.
  • Verifikasi dengan jendela ±1 step plus proteksi replay dan rate limiting.
  • Ia mencegah serangan kata sandi, tetapi tidak phishing real-time; passkey menutup celah itu.
  • Jaga jam server tetap tersinkron dengan NTP dan secret tetap terenkripsi dan privat.

Ingin melihat algoritma menghasilkan angka nyata dan memeriksa jendela verifikasi Anda sendiri? Buka generator TOTP / 2FA untuk menghitung, menyiapkan, dan memverifikasi kode sepenuhnya di browser Anda, dengan secret yang tidak pernah meninggalkan perangkat Anda.

Tag: Two-Factor Authentication TOTP Security Authentication 2FA

Artikel Terkait

Lihat semua artikel