One-Time Passwords (OTPs) are a cornerstone of modern authentication, used for login, password reset, and transaction verification. But not all OTPs are equal, and poor implementation can turn a security feature into a vulnerability. This article explains the cryptographic principles behind HOTP and TOTP, highlights common pitfalls, and shows how to use an OTP generator effectively.

What Is an OTP?
A One-Time Password (OTP) is a temporary, single-use code that authenticates a user for a specific session or transaction. Unlike static passwords, OTPs expire quickly and cannot be reused, making them resistant to replay attacks.
OTPs are typically delivered via SMS, email, or generated by an authenticator app. However, the underlying algorithms — HOTP (HMAC-based One-Time Password) and TOTP (Time-based One-Time Password) — define how the code is derived.
HOTP vs. TOTP: The Core Algorithms
HOTP (RFC 4226)
HOTP uses a counter that increments with each successful authentication. The server and client share a secret key, and both compute the OTP as:
HOTP(K, C) = Truncate(HMAC-SHA-1(K, C))
Where:
Kis the shared secret.Cis an 8-byte counter value.Truncateextracts a dynamic binary code and converts it to a decimal OTP of configurable length (typically 6–8 digits).
Pros: Works offline; no clock synchronization needed. Cons: Counter desynchronization if the user generates codes without authenticating (e.g., pressing "next" too many times).
TOTP (RFC 6238)
TOTP extends HOTP by replacing the counter with a time step (usually 30 seconds). The code is computed as:
TOTP(K, T) = HOTP(K, floor((current_time - T0) / X))
Where:
T0is the Unix epoch (0) or a custom start time.Xis the time step (e.g., 30 seconds).
Pros: Automatically resynchronizes; no counter drift. Cons: Requires accurate clock; vulnerable to clock skew (typically ±1 step allowed).
| Feature | HOTP | TOTP |
|---|---|---|
| Input | Counter (incremented) | Time (current Unix time) |
| Synchronization | Manual (counter resync) | Automatic (time-based) |
| Clock requirement | None | Requires accurate clock |
| Common use case | Hardware tokens | Authenticator apps |
Why OTP Implementation Matters
A well-implemented OTP system provides strong multi-factor authentication (MFA). However, common mistakes can render it insecure. Research (e.g., OTP-Hunter, 2025) has identified four major vulnerability classes:
- OTP Bombing — Repeatedly sending OTPs to a target, causing annoyance or resource exhaustion.
- OTP Brute-Force — Enumerating short OTP codes (e.g., 4–6 digits) due to missing rate limiting.
- OTP Leakage — Exposing OTPs in logs, URLs, or API responses.
- Resource Consumption — Server-side DoS via excessive OTP generation requests.
The SIM Swap Threat
SMS-based OTPs are particularly vulnerable to SIM swap attacks, where an attacker tricks a carrier into transferring the victim's phone number to a new SIM. Once in control, the attacker receives all SMS OTPs. This has led regulators like the Philippines' BSP to mandate phasing out SMS OTPs by 2026.
Common Implementation Pitfalls
- No rate limiting on OTP requests — Attackers can flood the server, causing denial of service or brute-force attempts.
- Short OTP length — 4-digit codes offer only 10,000 combinations; with no rate limit, they can be brute-forced in minutes.
- Predictable OTP seeds — Using weak random number generators (e.g.,
rand()instead ofcrypto/rand) makes the secret key guessable. - Storing OTPs in plaintext — Leaked databases expose active codes.
- Ignoring clock skew — TOTP implementations must allow a window (e.g., ±1 time step) to account for clock drift, but too large a window weakens security.
- No account lockout — After a few failed attempts, the account should be temporarily locked to prevent brute-force.
Worked Example: Generating and Verifying a TOTP
Let's walk through a complete example using Python (the pyotp library).
Step 1: Install the library
pip install pyotp
Step 2: Generate a secret key
import pyotp
# Generate a random base32 secret (16 characters = 80 bits)
secret = pyotp.random_base32()
print(f"Secret: {secret}")
Example output: JBSWY3DPEHPK3PXP
Step 3: Create a TOTP object and get the current code
totp = pyotp.TOTP(secret)
current_code = totp.now()
print(f"Current OTP: {current_code}")
Step 4: Verify the code
# Simulate user input
user_input = input("Enter OTP: ")
if totp.verify(user_input):
print("Valid OTP")
else:
print("Invalid OTP")
Step 5: Generate a QR code for the authenticator app
uri = totp.provisioning_uri(name="user@example.com", issuer_name="MyApp")
print(f"URI: {uri}")
# Use a QR code library (e.g., qrcode) to display this URI as a QR code.
The URI follows the otpauth:// scheme, which authenticator apps (Google Authenticator, Authy) can scan.
Using an OTP Generator Tool
Our OTP Generator lets you quickly generate HOTP/TOTP codes for testing or demonstration. You can specify the secret, algorithm, digits, and time step. This is especially useful for:
- Debugging OTP integration in your app.
- Generating test codes without setting up a full server.
- Understanding how different parameters affect the output.
Security Best Practices
- Use TOTP over HOTP for most applications — time synchronization is easier than counter management.
- Enforce rate limiting on OTP generation and verification endpoints.
- Use at least 6-digit codes (8 digits recommended).
- Set a short OTP validity window (e.g., 30 seconds for TOTP, or 5 minutes for email OTPs).
- Never log OTPs in plaintext.
- Use cryptographically secure random for secret generation.
- Implement account lockout after 3–5 failed attempts.
- Consider push-based authentication (e.g., in-app OTP) to avoid SIM swap risks.
FAQ
What is the difference between HOTP and TOTP?
HOTP uses a counter that increments with each use, while TOTP uses the current time as input. TOTP is more common in authenticator apps because it auto-synchronizes without user action.
Can OTPs be brute-forced?
Yes, if no rate limiting is applied. A 6-digit code has 1 million combinations; with 1000 requests per second, it can be cracked in ~16 minutes. Rate limiting and account lockout are essential.
Why is SMS OTP considered insecure?
SMS OTPs are vulnerable to SIM swap attacks, SS7 interception, and phishing. Regulators are increasingly mandating alternatives like app-based OTPs or biometrics.
How long should an OTP be valid?
For TOTP, 30 seconds is standard. For email or SMS, 5–10 minutes is common. Longer windows increase the risk of interception.
What is a "cloud trigger" in OTP fuzzing?
A cloud trigger is the last function in the app's code where parameters (e.g., phone number, OTP) are still unencrypted or unsigned. Fuzzing at this point can bypass encryption and integrity checks, revealing vulnerabilities.