💻 Uygulamalı Kriptografi · Mühendislik Derinlemesine İnceleme

TOTP Nasıl Çalışır: RFC 6238 Algoritmasını Python, JavaScript ve Go ile Uygulama

2F 2FA-Tool Güvenlik Araştırma Laboratuvarı · Ekim 2026'da Güncellendi · 16 dk okuma (2.700+ kelime)
💡 Mimari Özet ve Mühendislik Manifestosu

Modern web güvenliğindeki temel kriptografik protokoller arasında, RFC 6238 (TOTP: Zamana Dayalı Tek Kullanımlık Şifre Algoritması) matematiksel sadeliğiyle öne çıkar. Etkileşimli ağ el sıkışmaları, merkezi açık anahtar altyapısı (PKI) ve bulut gidiş-dönüşleri gerektirmez. Yalnızca paylaşılan bir Base32 gizli anahtarı ve fiziksel Unix zamanının sabit ilerleyişi ile istemci ve sunucu, %100 matematiksel kesinlikle her 30 saniyede bir aynı, geçici 6 haneli kimlik doğrulama belirteçlerini hesaplar.

Bu kapsamlı rehberde, üçüncü taraf kapalı kutu kütüphaneleri (pyotp veya otplib gibi) atlayarak eksiksiz RFC 6238 kriptografik iş hattını Python, JavaScript ve Go dillerinde sıfırdan inşa ediyor ve her uygulamayı resmi IETF test vektörlerine karşı doğruluyoruz.

01. Zaman Odaklı Kimlik Doğrulamanın Zarafeti

TOTP'yi anlamak için öncelikle selefini incelememiz gerekir: RFC 4226 HOTP (HMAC Tabanlı Tek Kullanımlık Şifre).

HOTP'de istemci ve sunucu bir olay sayacı $C$ paylaşır. Kullanıcı her belirteç oluşturduğunda sayaç artar ($C = C + 1$) ve belirteç $\text{HMAC-SHA-1}(K, C)$ üzerinden türetilir. Matematiksel olarak sağlam olsa da HOTP operasyonel bir zayıflıktan muzdaripti: sayaç senkronizasyonunun bozulması. Bir kullanıcı cebindeki donanım anahtarının düğmesine yanlışlıkla beş kez basarsa, istemci sayacı $C=6$ değerine ulaşırken kimlik doğrulama sunucusu $C=1$ değerinde kalır ve bu da sürekli oturum açma reddine yol açardı.

⏱️ RFC 6238'in Çığır Açan Yeniliği: Olay Sayaçlarını Epoch Zamanıyla Değiştirmek

Mayıs 2011'de N. M'Raihi, S. Machani, M. Pei ve J. Rydell tarafından yayımlanan RFC 6238, artan tam sayıyı standartlaştırılmış Unix epoch zamanı ile değiştirerek sayaç senkronizasyon sorununu çözdü. Fiziksel zaman dünya genelinde eşit şekilde ilerlediğinden, hem istemci hem de sunucu tek bir ağ paketi bile göndermeden dahili sayaçlarını aynı anda ilerletir.

02. TOTP'nin 5 Kriptografik Matematik Adımı

16 karakterlik bir Base32 tohumunu (ör. JBSWY3DPEHPK3PXP) 6 haneli dönen bir koda (ör. 284915) dönüştürmek beş hassas kriptografik aşama içerir:

1 Adım 1: Base32 Gizli Anahtar Çözme (Decoding)

Borsa kayıt anahtarları, A-Z ve 2-7 alfabesi kullanılarak (0, 1, 8, 9 gibi belirsiz karakterler hariç tutularak) RFC 4648 Base32 ile kodlanır. Algoritma önce bu dizeyi normalize eder, boşlukları temizler ve uzunluğu 8'in katı olacak şekilde dolgu (=) uygular, ardından onu ham ikili (binary) anahtar baytlarına $K$ çözer.

2 Adım 2: Zaman Adımı Hesaplaması (Sayaç $T$)

Geçerli Unix zaman damgası (1 Ocak 1970 UTC'den bu yana geçen saniye) tam sayı sayacı $T$'ye eşlenir:

T = floor( (CurrentTime - T0) / X )

Standart parametreler $T_0 = 0$ ve adım süresi $X = 30$ saniyedir. Bu tam sayı $T$ daha sonra 8 baytlık big-endian 64 bit işaretsiz tam sayı olarak kodlanır.

3 Adım 3: HMAC-SHA1 Özet (Hash) Hesaplaması

Ham anahtar baytları $K$ gizli anahtar olarak kullanılarak, 8 baytlık big-endian zaman arabelleği üzerinde HMAC-SHA1 özeti hesaplanır ve 20 baytlık (160 bit) bir mesaj kimlik doğrulama özeti $HS$ elde edilir:

HS = HMAC-SHA-1(Key, T)
4 Adım 4: Dinamik Kırpma (Dynamic Truncation - DT)

20 ham baytı bir tam sayıya dönüştürmek için RFC 4226 Dinamik Kırpmayı tanımlar:

  • Ofseti Çıkarın: Son bayt $HS[19]$'u inceleyin ve alt 4 bitini çıkarmak için 0x0F ile maskeleyin; bu, 0 ile 15 arasında bir ofset değeri verir: Offset = HS[19] & 0x0F;
  • 4 Baytı Dilimleyin: Ofsetten başlayarak ardışık 4 baytı çıkarın: $P = HS[Offset \dots Offset+3]$;
  • İşaret Bitini Maskeleyin: İşaretli tam sayı taşması belirsizliğini önlemek için en anlamlı biti (MSB) 0x7F ile maskeleyin:
    BinaryCode = ((P[0] & 0x7F) << 24) | ((P[1] & 0xFF) << 16) | ((P[2] & 0xFF) << 8) | (P[3] & 0xFF)
5 Adım 5: $N$ Haneye Modulo İndirgemesi

Son olarak, standart 6 haneli belirteçler için BinaryCode değerini modulo $10^6$ (1.000.000) ile indirgeyin. Tutarlı bir 6 karakterli çıktı dizesi sağlamak için sonucun solunu sıfırlarla doldurun:

OTP = BinaryCode mod 10^6

03. Çalıştırılabilir Kod 1: Python 3 Standart Kütüphanesi

Bu eksiksiz uygulama, hiçbir pip paketi gerektirmeden Python'un yerleşik base64, struct, hmac ve hashlib modüllerini kullanır:

totp_rfc6238.py Python 3.8+ (Sıfır Bağımlılık)
import base64
import hashlib
import hmac
import struct
import time

def generate_totp(secret_base32: str, timestamp: int = None, step: int = 30, digits: int = 6) -> str: """ Generate an RFC 6238 compliant TOTP token. :param secret_base32: Shared secret key in Base32 format (e.g., ‘JBSWY3DPEHPK3PXP’) :param timestamp: Unix epoch timestamp in seconds. Defaults to time.time() :param step: Time-step window in seconds (default: 30) :param digits: Number of output digits (default: 6) :return: Left-padded OTP string (e.g., ‘045812’) """ if timestamp is None: timestamp = int(time.time())

1. Normalize Base32 string and append ’=’ padding characters

normalized_secret = secret_base32.replace(” ”, "").upper() padding_needed = (8 - len(normalized_secret) % 8) % 8 normalized_secret += ”=” * padding_needed key_bytes = base64.b32decode(normalized_secret)

2. Compute 64-bit big-endian time-step counter T

time_counter = timestamp // step time_bytes = struct.pack(“>Q”, time_counter) # 8 bytes big-endian unsigned long long

3. Calculate HMAC-SHA1 digest (20 bytes)

hmac_digest = hmac.new(key_bytes, time_bytes, hashlib.sha1).digest()

4. Perform Dynamic Truncation

offset = hmac_digest[-1] & 0x0F # Lower 4 bits of the 20th byte

Unpack 4 bytes at offset and clear MSB sign bit

binary_code = struct.unpack(“>I”, hmac_digest[offset : offset + 4])[0] & 0x7FFFFFFF

5. Modulo arithmetic and zero-fill formatting

otp_code = binary_code % (10 ** digits) return str(otp_code).zfill(digits)

if name == “main”: test_secret = “JBSWY3DPEHPK3PXP” print(f”Generated TOTP Code: {generate_totp(test_secret)}”)

04. Çalıştırılabilir Kod 2: JavaScript ve Node.js Crypto

Yerel crypto.createHmac kullanarak arka uç Node.js çalışma zamanları için saf JavaScript uygulaması:

totp_node.js Node.js Yerel Crypto
const crypto = require('crypto');

// RFC 4648 Base32 Decoder in Pure JavaScript function base32Decode(base32Str) { const alphabet = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ234567’; const cleaned = base32Str.replace(/=+$/, ”).toUpperCase().replace(/\s+/g, ”); let bits = 0, value = 0, index = 0; const output = new Uint8Array(Math.floor((cleaned.length * 5) / 8));

for (let i = 0; i < cleaned.length; i++) { const val = alphabet.indexOf(cleaned[i]); if (val === -1) continue; value = (value << 5) | val; bits += 5; if (bits >= 8) { output[index++] = (value >>> (bits - 8)) & 255; bits -= 8; } } return Buffer.from(output.buffer, 0, index); }

function generateTOTP(secretBase32, timestamp = Math.floor(Date.now() / 1000), step = 30, digits = 6) { const key = base32Decode(secretBase32); const counter = Math.floor(timestamp / step);

// Allocate 8-byte buffer in Big-Endian format const buffer = Buffer.alloc(8); buffer.writeBigUInt64BE(BigInt(counter), 0);

// Compute HMAC-SHA1 const hmac = crypto.createHmac(‘sha1’, key).update(buffer).digest();

// Dynamic Truncation const offset = hmac[hmac.length - 1] & 0x0f; const binary = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | (hmac[offset + 3] & 0xff);

const otp = binary % (10 ** digits); return otp.toString().padStart(digits, ‘0’); }

console.log(“Calculated TOTP:”, generateTOTP(“JBSWY3DPEHPK3PXP”));

W3C Web Crypto API ile İstemci Taraflı Tarayıcı Yürütmesi:

%100 çevrimdışı güvenlik arayan modern web uygulamaları için (2FA-Tool'a güç veren mimarinin birebir aynısı), W3C Web Crypto API üçüncü taraf npm paketlerine ihtiyaç duymadan doğrudan tarayıcı sanal alanında (sandbox) eşzamansız, donanım hızlandırmalı kriptografik temel öğeler sağlar:

webcrypto_totp.js Tarayıcı W3C Web Crypto API
async function generateBrowserTOTP(secretBase32, timestamp = Math.floor(Date.now() / 1000)) {
const keyBytes = base32Decode(secretBase32);
const counter = Math.floor(timestamp / 30);

// 1. Import raw secret key into SubtleCrypto const cryptoKey = await window.crypto.subtle.importKey( “raw”, keyBytes, { name: “HMAC”, hash: { name: “SHA-1” } }, false, [“sign”] );

// 2. Encode 8-byte Big-Endian counter buffer const buffer = new ArrayBuffer(8); const view = new DataView(buffer); view.setBigUint64(0, BigInt(counter), false); // false = Big-Endian

// 3. Perform cryptographic HMAC sign const signature = await window.crypto.subtle.sign(“HMAC”, cryptoKey, buffer); const hmacResult = new Uint8Array(signature);

// 4. Dynamic Truncation const offset = hmacResult[hmacResult.length - 1] & 0x0f; const binary = ((hmacResult[offset] & 0x7f) << 24) | ((hmacResult[offset + 1] & 0xff) << 16) | ((hmacResult[offset + 2] & 0xff) << 8) | (hmacResult[offset + 3] & 0xff);

const otp = binary % 1000000; return otp.toString().padStart(6, ‘0’); }

05. Çalıştırılabilir Kod 3: Üretim Düzeyi Golang (Go)

Yüksek işlem hacimli mikro servisler, Go'nun kendini kanıtlamış standart kütüphanesinden yararlanır:

totp.go Go 1.20+ Standart Kütüphanesi
package main

import ( “crypto/hmac” “crypto/sha1” “encoding/base32” “encoding/binary” “fmt” “math” “strings” “time” )

// GenerateTOTP computes standard RFC 6238 time-based tokens func GenerateTOTP(secretBase32 string, timestamp int64, step int64, digits int) (string, error) { // 1. Sanitize Base32 seed and decode cleaned := strings.ToUpper(strings.ReplaceAll(secretBase32, ” ”, "")) key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(cleaned) if err != nil { key, err = base32.StdEncoding.DecodeString(cleaned) if err != nil { return "", err } }

// 2. Encode 64-bit Big-Endian time-step counter counter := uint64(timestamp / step) buf := make([]byte, 8) binary.BigEndian.PutUint64(buf, counter)

// 3. Compute HMAC-SHA1 mac := hmac.New(sha1.New, key) mac.Write(buf) hash := mac.Sum(nil)

// 4. Dynamic Truncation offset := hash[len(hash)-1] & 0x0f binaryCode := binary.BigEndian.Uint32(hash[offset:offset+4]) & 0x7fffffff

// 5. Modulo arithmetic mod := uint32(math.Pow10(digits)) otp := binaryCode % mod

format := fmt.Sprintf(“%%0%dd”, digits) return fmt.Sprintf(format, otp), nil }

func main() { code, err := GenerateTOTP(“JBSWY3DPEHPK3PXP”, time.Now().Unix(), 30, 6) if err != nil { panic(err) } fmt.Printf(“Golang Computed TOTP: %s\n”, code) }

06. RFC 6238 Ek B Test Vektörleri Doğrulaması

Standart uyumluluğunu kanıtlamak için her kriptografik uygulama, IETF RFC 6238 Ek B'de yayımlanan resmi test vektörlerine karşı doğrulanmalıdır.

Resmi test paketi, Base32'de şu şekilde kodlanan "12345678901234567890" (20 bayt) ASCII sırrını kullanır:
GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ (30 saniyelik adımlarla 8 haneli çıktılar için değerlendirilmiştir):

Unix Epoch Zaman Damgası UTC Tarih ve Saat RFC Resmi Beklenen Kod Uygulama Sonucu
59 1970-01-01 00:00:59 94287082 ✓ BAŞARILI (Tam Eşleşme)
1111111109 2005-03-18 01:58:29 07081804 ✓ BAŞARILI (Tam Eşleşme)
1111111111 2005-03-18 01:58:31 14050471 ✓ BAŞARILI (Tam Eşleşme)
1234567890 2009-02-13 23:31:30 89005924 ✓ BAŞARILI (Tam Eşleşme)
2000000000 2033-05-18 03:33:20 69279037 ✓ BAŞARILI (Tam Eşleşme)

07. Üretim Mühendisliği: Zaman Kayması, Tekrar Oynatma ve Hız Sınırları

Yüksek erişilebilirlikli kurumsal kimlik doğrulama sistemlerinde TOTP doğrulamasını devreye alırken üç güvenlik gereksinimi zorunlu kılınmalıdır:

Kural 1 Saat Sapması Tolerans Penceresi

Küçük mobil saat sapmaları nedeniyle sunucular [T - 1, T, T + 1] penceresini değerlendirmelidir (önceki 30 sn, geçerli 30 sn ve sonraki 30 sn kontrol edilir). Pencereyi asla $\pm 1$ adımın ötesine genişletmeyin; çünkü her ek adım, tekrar oynatma (replay) saldırı yüzeyini 30 saniye genişletir.

Kural 2 Tek Kullanımlık Belirteç Zorunluluğu (Tekrar Oynatma Önleme)

Bir belirteç başarıyla kullanıldıktan hemen sonra geçersiz kılınmalıdır. Last_Verified_Time_Step değerini Redis'te veya bellek içi önbellekte saklayın. Bir istemci aynı 30 saniyelik pencere içinde aynı belirteci göndermeye çalışırsa, ortadaki adam (MitM) tekrar oynatma saldırılarını etkisiz hale getirmek için isteği reddedin.

Kural 3 Kaba Kuvvet Hız Sınırlandırması ve Sabit Zamanlı Karşılaştırma

6 haneli kodlar yalnızca 1.000.000 permütasyonu kapsadığından, hız sınırı olmayan uç noktalar hızla kaba kuvvet (brute-force) saldırısına uğrayabilir. Ardışık 3 başarısızlığın ardından artan gecikmeler ve hesap kilitlemeleri uygulayın. Kod içinde, yan kanal zamanlama analizini engellemek için karşılaştırmaları sabit zamanlı eşitlik işlevleri (ör. Go'da crypto/subtle.ConstantTimeCompare veya Node.js'de crypto.timingSafeEqual) kullanarak gerçekleştirin.

08. Sıkça Sorulan Sorular (SSS)

S1: SHA-1 kriptografik olarak kırıldığı halde TOTP neden hala varsayılan olarak HMAC-SHA1 kullanıyor? ▼

Ham SHA-1'e yönelik teorik çakışma (collision) saldırıları HMAC yapısını (RFC 2104) kırmaz. HMAC'te, temel özet işlevi iç ve dış gizli anahtarların içine yerleştirilmiştir ve bu da çakışma tabanlı saldırıları etkisiz hale getirir. Günümüzde HMAC-SHA1, 2FA için güvenli olmaya devam etmektedir. Ayrıca daha yüksek güvence gerekiyorsa RFC 6238, HMAC-SHA256 ve HMAC-SHA512'yi tam olarak destekler.

S2: Saat dilimleri veya Yaz Saati Uygulaması (DST) TOTP senkronizasyonunu etkiler mi? ▼

Hayır. Unix epoch zamanı, 1970-01-01 00:00:00 UTC'den bu yana geçen saniye olarak tanımlanır. Saat dilimlerinden, artık saniyelerden ve yaz saati geçişlerinden tamamen bağımsızdır. Herhangi bir anda Tokyo'daki bir sunucu ile Kaliforniya'daki bir akıllı telefon birebir aynı tam sayı zaman damgasına sahiptir.

S3: Steam Guard 5 harfli kimlik doğrulaması RFC 6238'den nasıl farklıdır? ▼

Steam Guard, RFC 6238'e dayalı özel (tescilli) bir varyanttır. Birebir aynı 30 saniyelik zaman adımını ve HMAC-SHA1 hesaplamasını kullanır, ancak son indirgeme aşamasında çıkarılan tam sayıyı 5 karakterli bir dize üretmek üzere özel bir 26 karakterlik alfanümerik karakter dizisi (23456789BCDFGHJKMNPQRTVWXY) üzerinden eşler.

RFC 6238 Uyumlu · W3C Web Crypto API İstemci Taraflı Hesaplama

%100 İstemci Taraflı TOTP Kod Üretimini Canlı Deneyimleyin

2FA-Tool, W3C Web Crypto API kullanarak bu kriptografik iş hattını doğrudan tarayıcınızın içinde uygular. Sıfır sunucu iletimi, sıfır belirteç depolama, anında doğrulama.