One-Time Passwords (OTPs) are everywhere: login, password reset, payment confirmation. They seem simple – a short code sent to your phone – but their security depends heavily on the delivery channel. SMS OTPs, once the gold standard, are now a prime attack surface. This article dives into the vulnerabilities of SMS-based OTPs, explains why in-app OTPs are gaining traction, and provides concrete best practices for developers. Try generating test OTPs with our OTP Generator to see the difference in implementations.

The Three Main Attacks on SMS OTPs
SMS OTPs rely on the assumption that "your phone number = you." Attackers have three proven ways to break that assumption:
SIM Swap
An attacker convinces (or bribes) a mobile carrier to transfer your phone number to a SIM card they control. Once done, all your SMS – including OTPs – go to their device. This is a well-known attack, especially in regions with weak carrier verification.
SS7 Interception
Signaling System No. 7 (SS7) is the protocol that lets telecom networks talk to each other. It was designed decades ago with minimal security. Attackers can exploit SS7 vulnerabilities to intercept SMS messages in transit, without touching your phone. This technique is now commoditized in the underground.
Phishing
The simplest attack: a fake website or app that mimics a bank or service, tricking you into entering the OTP you just received. The attacker then uses that OTP in real-time to complete a transaction.
These attacks are not theoretical. In the Philippines, the central bank (BSP) recorded 70,000 financial fraud complaints in 2024 alone, leading to a regulatory ban on SMS OTPs for high-risk transactions by mid-2026.
Why In-App OTPs Are More Secure
An in-app OTP is generated and displayed inside the authenticator app itself (e.g., Google Authenticator, Authy, or a built-in bank app). It never travels over the cellular network. This eliminates SIM swap and SS7 interception entirely. Phishing is also much harder because the code is tied to the device and app session.
| Feature | SMS OTP | In-App OTP |
|---|---|---|
| Delivery channel | Cellular network (untrusted) | App push / local generation (controlled) |
| Susceptible to SIM swap | Yes | No |
| Susceptible to SS7 interception | Yes | No |
| Phishable | High (code sent to phone, can be forwarded) | Low (code bound to device) |
| User experience | Slow, requires switching apps | Fast, one-tap confirmation |
| Offline capability | No | Yes (TOTP) |
When to Use Which OTP Method
- SMS OTP is still acceptable for low-risk actions like verifying a phone number during registration. But it should never be used for high-risk transactions (money transfers, password changes, critical account updates).
- In-App OTP (especially TOTP – Time-based One-Time Password) is the current best practice for two-factor authentication (2FA). It is free, open-standard (RFC 6238), and works offline.
- Push-based in-app OTP (where the app receives a push notification and you tap "Approve") offers even better UX and security, as it requires an active app session.
Worked Example: Implementing TOTP In-App
Let's walk through a minimal TOTP implementation in Python. This is the core of most in-app authenticators.
import hmac
import hashlib
import struct
import time
def hotp(secret: bytes, counter: int, digits: int = 6) -> str:
"""Generate an HMAC-based one-time password (HOTP)."""
msg = struct.pack('>Q', counter)
h = hmac.new(secret, msg, hashlib.sha1).digest()
offset = h[-1] & 0x0f
truncated = struct.unpack('>I', h[offset:offset+4])[0] & 0x7fffffff
return str(truncated % (10 ** digits)).zfill(digits)
def totp(secret: bytes, digits: int = 6, interval: int = 30) -> str:
"""Generate a time-based one-time password (TOTP)."""
counter = int(time.time()) // interval
return hotp(secret, counter, digits)
# Example usage
secret = b'12345678901234567890' # In production, generate securely
print(totp(secret)) # e.g., "287082"
Key points:
- The
secretmust be generated securely (e.g.,os.urandom(20)) and shared with the user via QR code (usingotpauth://URI). - The counter is the current Unix timestamp divided by 30 seconds. This gives a 30-second window.
- The code uses HMAC-SHA1, which is the default for TOTP. SHA256 or SHA512 can also be used.
To verify, the server computes the same TOTP and compares it to the user's input. A common practice is to allow a small time skew (e.g., ±1 interval) to account for clock drift.
Common Pitfalls in OTP Implementation
- Hardcoded secrets: Never embed secrets in client-side code. Use a QR code to transfer the secret securely.
- No rate limiting: Without rate limiting, an attacker can brute-force a 6-digit OTP (1 million possibilities) in minutes. Implement exponential backoff or CAPTCHA after a few failures.
- Long validity windows: A 10-minute OTP window is too long. Stick to 30 seconds for TOTP, or 1-2 minutes for email/SMS.
- Reusing OTPs: Each OTP should be single-use. Once verified, it must not be accepted again (even within the time window).
- Logging OTPs: Never log OTP values. They are sensitive secrets.
- Ignoring clock skew: TOTP requires accurate time. Allow a small drift (e.g., ±1 interval) on the server.
FAQ
Why are SMS OTPs still used if they are insecure?
SMS OTPs are easy to implement and work on any phone without an app. Many legacy systems rely on them. However, regulators are increasingly banning them for high-risk transactions.
What is the difference between HOTP and TOTP?
HOTP (HMAC-based) uses a counter that increments with each use. TOTP uses time as the counter. TOTP is more common for 2FA because it doesn't require the server to track the counter.
Can in-app OTPs be phished?
Yes, but it's harder. An attacker would need to trick the user into entering the code on a fake site in real-time. Push-based approvals (where the user taps "Approve") are even more resistant.
Should I generate OTPs on the server or client?
For TOTP, the secret is shared at enrollment, then both sides generate the same code independently. The server must verify, but generation can happen on the client.
What is the best OTP delivery method for high-security apps?
In-app TOTP or push-based approval, combined with biometric verification (fingerprint/Face ID) on the device. Avoid SMS entirely for critical operations.