正在加载,请稍候…

OTP Best Practices: From SMS to In-App Authentication

Learn how one-time passwords work, why SMS OTP is insecure, and how in-app OTP improves security. Includes a worked example and comparison table.

One-time passwords (OTPs) are a cornerstone of multi-factor authentication (MFA). They provide a second layer of verification beyond your password, typically sent via SMS or generated by an authenticator app. But not all OTPs are created equal. SMS OTPs, once considered secure, are now vulnerable to SIM swapping, SS7 interception, and phishing. In-app OTPs — generated and displayed inside the same application you're logging into — offer a more secure, user-friendly alternative.

person holding phone showing OTP notification

What Is an OTP and How Is It Generated?

An OTP is a temporary, numeric or alphanumeric code valid for a single login session or transaction. It is generated using a seed secret (shared between the server and the user's device) combined with either:

  • Time-based (TOTP): The current time (usually in 30-second intervals) is hashed with the seed to produce a code. The server and device must have roughly synchronized clocks.
  • Counter-based (HOTP): An incrementing counter is used instead of time. Each successful login increments the counter on both sides.

Most modern apps use TOTP because it doesn't require state synchronization. The algorithm is defined in RFC 6238 (TOTP) and RFC 4226 (HOTP).

Worked Example: TOTP Generation in Python

import hmac
import hashlib
import struct
import time

# Shared secret (base32-encoded, as in Google Authenticator)
secret = "JBSWY3DPEHPK3PXP"  # example secret

# Decode the secret
key = base64.b32decode(secret, casefold=True)

# Get the current time step (30 seconds)
timestep = int(time.time()) // 30

# Pack the timestep into 8 bytes (big-endian)
timestep_bytes = struct.pack(">Q", timestep)

# Compute HMAC-SHA1
hash = hmac.new(key, timestep_bytes, hashlib.sha1).digest()

# Dynamic truncation: get last 4 bits of the hash as offset
offset = hash[-1] & 0x0F

# Extract 4 bytes from the hash at the offset
binary_code = struct.unpack(">I", hash[offset:offset+4])[0] & 0x7FFFFFFF

# Generate a 6-digit code
otp = binary_code % 10**6
print(f"Your OTP is: {otp:06d}")

This is exactly how apps like Google Authenticator and Authy work. The server stores the same secret and performs the same calculation to verify the code.

SMS OTP vs. In-App OTP: A Security Comparison

Feature SMS OTP In-App OTP
Delivery channel Cellular network (unencrypted SS7) Encrypted push notification or in-app generation
Phishing resistance Low — code can be intercepted or relayed High — tied to the device and app session
SIM swap protection None Strong — requires device possession
User experience Switch apps, wait for SMS One-tap confirmation, no app switch
Cost Pay-per-SMS (especially international) Free after initial development
Regulatory trend Being phased out (e.g., Philippines BSP Circular 1213) Becoming standard for high-risk transactions

Why SMS OTP Is Being Phased Out

Attackers have three main vectors against SMS OTP:

  • SIM swap: Fraudulently transfer the victim's phone number to the attacker's SIM.
  • SS7 interception: Exploit vulnerabilities in telecom signaling protocols to intercept SMS.
  • Phishing: Trick the user into entering the OTP on a fake site.

Regulators worldwide are taking notice. The Philippines' BSP Circular 1213 mandates that all banks and e-wallets stop using SMS OTP for high-risk transactions by June 30, 2026. Similar moves are expected in other Southeast Asian countries.

In-App OTP: How It Works and Why It's Better

In-app OTP means the code is generated or displayed within the application you are already using (e.g., a banking app). There are two common implementations:

  1. Push-based: The server sends a push notification to the registered device. The user taps "Approve" to authenticate. No code is displayed.
  2. TOTP in-app: The app generates a TOTP code using a stored secret, similar to an authenticator app, but embedded in the same app.

Both approaches eliminate the cellular network from the trust chain. The code never leaves the device, and the user doesn't need to switch apps.

Implementation Considerations

  • Device binding: The secret must be tied to a specific device, typically using hardware-backed storage (Android Keystore, iOS Secure Enclave).
  • Push notification permissions: Users must enable notifications for the app. Without them, push-based OTP won't work.
  • Fallback mechanism: Provide a backup method (e.g., recovery codes) in case the device is lost.

Common Pitfalls

  • Using SMS OTP for high-risk actions: Transactions like fund transfers, password changes, or adding new payees should never rely solely on SMS.
  • Storing secrets in plaintext: The server must encrypt the shared secret at rest. If the database is breached, all OTPs are compromised.
  • Ignoring clock drift: TOTP requires synchronized clocks. Allow a small window (e.g., ±1 time step) to accommodate drift.
  • Not limiting OTP attempts: An attacker can brute-force a 6-digit code (1 in 1,000,000 chance per attempt). Implement rate limiting or lockout after 3-5 failures.
  • Sending OTPs over unencrypted channels: Always use HTTPS for any OTP delivery or verification endpoint.

FAQ

What is the difference between TOTP and HOTP?

TOTP is time-based (code changes every 30 seconds), while HOTP is counter-based (code changes only after successful use). TOTP is more common because it doesn't require the server to track a counter.

Can in-app OTP be phished?

It's much harder. Since the code is generated inside the legitimate app, a phishing site cannot easily obtain it. However, if the attacker tricks the user into installing a malicious app that mimics the real one, they could intercept the code. Device integrity checks help mitigate this.

Is SMS OTP completely useless?

Not entirely. It can still be used for low-risk actions like verifying a phone number during registration. But for authentication or transaction authorization, it should be replaced.

How do I migrate from SMS OTP to in-app OTP?

  1. Implement a TOTP generator in your app (or use push notifications).
  2. For existing users, prompt them to enable in-app OTP during their next login.
  3. Provide a grace period where SMS OTP still works, then phase it out.
  4. Offer recovery codes for lost devices.

What does the future hold for OTP?

Biometrics (fingerprint, face recognition) and behavioral authentication (typing patterns, device movement) are emerging as stronger alternatives. OTP may eventually be replaced entirely, but for now, in-app OTP is a significant improvement over SMS.

Try generating a TOTP code yourself with our OTP generator tool.