SMS OTPs are the de facto standard for two-factor authentication in India — from banking and fintech to e-commerce and healthcare. But as adoption has grown, so have the attack surfaces. This guide covers every major OTP SMS security threat and the implementation patterns that mitigate them.
Why OTP SMS Security Matters in India
RBI mandates SMS-based two-factor authentication for most online financial transactions in India. NPCI's UPI, net banking, and card-not-present e-commerce transactions all rely on OTP verification. A compromised OTP flow doesn't just expose a single account — it can result in regulatory penalties, customer compensation liabilities, and permanent reputational damage.
Understanding and hardening every layer of your OTP implementation is not optional. It is a compliance and business continuity requirement.
Threat Model: What Can Go Wrong
Before implementing countermeasures, understand the attack surface:
| Threat | Attack Vector | Likelihood |
|---|---|---|
| OTP interception | SS7 protocol exploit | Low (targeted) |
| SIM swap fraud | Social engineering at telco | Medium |
| OTP bombing | Scripted request flooding | High |
| Phishing | Fake login pages | High |
| Brute force | Guessing short OTPs | Medium |
| Client-side exposure | OTP logged or leaked in frontend | Medium |
| Template injection | Malicious input in OTP message | Low |
Best Practice 1 — Generate Cryptographically Secure OTPs
Never use Math.random() or Date.now() to generate OTPs. These are not cryptographically secure and can be predicted or reverse-engineered.
Correct Approach (Node.js)
import { randomInt } from "crypto"; // Node.js built-in
function generateOtp(digits = 6): string {
const min = Math.pow(10, digits - 1);
const max = Math.pow(10, digits) - 1;
return randomInt(min, max + 1).toString();
}
Python
import secrets
def generate_otp(digits: int = 6) -> str:
lower = 10 ** (digits - 1)
upper = 10 ** digits
return str(secrets.randbelow(upper - lower) + lower)
Use 6 digits minimum. A 4-digit OTP has only 10,000 combinations — trivially brute-forceable. 6 digits with a lockout after 3 failed attempts is the RBI baseline.
Best Practice 2 — Store OTP Hashes, Never Plaintext
Your database should never store the OTP in plaintext. Store a bcrypt or Argon2 hash alongside the expiry timestamp.
import bcrypt from "bcrypt";
async function storeOtp(mobile: string, otp: string) {
const hash = await bcrypt.hash(otp, 10);
const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
await db.otps.upsert({
where: { mobile },
create: { mobile, hash, expiresAt, attempts: 0 },
update: { hash, expiresAt, attempts: 0 },
});
}
async function verifyOtp(mobile: string, input: string): Promise<boolean> {
const record = await db.otps.findUnique({ where: { mobile } });
if (!record || record.expiresAt < new Date()) return false;
if (record.attempts >= 3) throw new Error("OTP locked after 3 failed attempts");
const isValid = await bcrypt.compare(input, record.hash);
if (isValid) {
await db.otps.delete({ where: { mobile } }); // single use
} else {
await db.otps.update({ where: { mobile }, data: { attempts: { increment: 1 } } });
}
return isValid;
}
Best Practice 3 — Enforce Short Expiry Windows
| Use Case | Recommended Expiry |
|---|---|
| Financial transaction OTP | 3–5 minutes |
| Login / account access | 5–10 minutes |
| Registration verification | 10–15 minutes |
| Password reset | 10 minutes |
Always invalidate the OTP on first use. An OTP that can be used multiple times within its validity window is a significant vulnerability — a stolen OTP remains exploitable for the full window.
Best Practice 4 — Rate-Limit OTP Requests (OTP Bombing Prevention)
OTP bombing is one of the most common attacks against SMS-based auth in India. Implement multi-layer rate limiting:
Per-Number Limit
import { createClient } from "redis";
const redis = createClient();
async function checkOtpRateLimit(mobile: string) {
const key = `otp_req:${mobile}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 3600); // 1-hour window
if (count > 5) {
throw new Error("Too many OTP requests. Try again in 1 hour.");
}
}
Per-IP Limit
Apply a secondary limit on the requesting IP address to prevent distributed attacks using multiple mobile numbers.
CAPTCHA on the Request Form
Add Google reCAPTCHA v3 or hCaptcha to your OTP request form. This blocks automated scripted submissions without adding friction for genuine users.
Best Practice 5 — Prevent Brute Force with Attempt Locking
Allow a maximum of 3–5 incorrect OTP entries before locking the session. Implement exponential backoff for repeated lock events from the same number.
const MAX_ATTEMPTS = 3;
const LOCKOUT_SECONDS = 900; // 15 minutes
async function validateAttempts(mobile: string) {
const key = `otp_lock:${mobile}`;
const locked = await redis.get(key);
if (locked) throw new Error("Account temporarily locked. Try again later.");
}
async function recordFailedAttempt(mobile: string, currentAttempts: number) {
if (currentAttempts + 1 >= MAX_ATTEMPTS) {
await redis.set(`otp_lock:${mobile}`, "1", { EX: LOCKOUT_SECONDS });
}
}
Best Practice 6 — Mitigate SIM Swap Fraud
SIM swap fraud — where an attacker convinces a telecom operator to reassign a victim's number to a new SIM — is a growing threat in India, particularly for banking and fintech applications.
Detection Strategies
- Monitor SIM change events via TRAI's MNRP APIs or your telecom partner's hooks. If a SIM was recently swapped (within 24–72 hours), enforce an additional verification step.
- Device binding — tie the OTP flow to a specific registered device. If the OTP is being requested from a new device, require additional identity verification before sending.
- Transaction cooling period — after a SIM swap event is detected on a customer's number, block high-value transactions for 24–48 hours.
Offer App-Based TOTP as an Alternative
For high-risk operations, offer users the option to authenticate via TOTP (Time-based One-Time Password) using an authenticator app (Google Authenticator, Authy). TOTP is immune to SIM swap attacks because the secret is stored on the device, not delivered over the telecom network.
Best Practice 7 — Craft Phishing-Resistant SMS Templates
Your OTP SMS template itself is a security control. A well-written template makes it harder for phishing sites to impersonate you.
Good Template
747291 is your Get Click Media login OTP. Valid for 5 minutes.
NEVER share this OTP with anyone — we will never ask for it. — GCMDIA
Why It Works
- States the platform name clearly — harder to replicate convincingly
- Includes an explicit "never share" warning
- Short expiry communicated to the user
- Sender ID is consistent and registered on DLT
Bad Template (Avoid)
Your OTP is 747291. — GCMDIA
This provides no context, no expiry, and no anti-phishing cue. A fake SMS with the same format would be indistinguishable to the recipient.
Best Practice 8 — Secure Your API Credentials
Your OTP SMS API key is as sensitive as a database password. Mishandling it can expose your entire user base to OTP spoofing.
- Store API keys in environment variables, never in source code
- Rotate API keys periodically (every 90 days minimum)
- Use separate API keys for development and production environments
- Restrict API key permissions to only the required operations (send-only, no admin access)
- Monitor your SMS provider dashboard for unexpected volume spikes — a sudden surge may indicate a compromised key
Best Practice 9 — Log and Monitor OTP Events
Build an audit trail for every OTP event — generated, delivered, verified, failed, expired. This serves two purposes:
- Fraud investigation — you can reconstruct the exact OTP flow for any transaction under dispute
- Anomaly detection — spikes in failed verifications, unusual geographic patterns, or abnormal volume from a single number are early signals of an active attack
Minimum Log Fields
{
"event": "otp_generated",
"mobile": "+91XXXXXXXXXX",
"ip": "203.0.113.42",
"userAgent": "Mozilla/5.0...",
"timestamp": "2026-06-19T10:23:45Z",
"purpose": "login",
"expiresAt": "2026-06-19T10:33:45Z"
}
Never log the OTP value itself — only the event metadata.
Best Practice 10 — Use the Transactional Route with a Reputable Provider
The telecom route your OTP travels through directly affects delivery reliability and security. A low-cost shared route may co-mingle your messages with promotional traffic, introducing latency and increasing the risk of delays that push users to request multiple OTPs.
Use a provider that offers:
- A dedicated transactional route (DND-exempt, priority queue)
- Guaranteed delivery receipts via webhooks
- DLT-registered sender IDs and templates
- Sub-3-second delivery SLA
Compliance Checklist
Before going live with OTP SMS in India:
- DLT entity registration complete and approved
- Transactional sender ID registered and approved
- OTP message template registered and approved — no unapproved variable patterns
- OTP generated using
crypto.randomIntorsecrets.randbelow - OTP stored as a hash (bcrypt/Argon2), not plaintext
- Maximum 3–5 attempt lockout implemented
- Rate limiting active — max 5 OTP requests per number per hour
- OTP expires in 10 minutes or less; invalidated after single use
- SIM swap monitoring or device binding in place for high-risk flows
- API credentials stored in environment variables, not in code
- OTP events logged (without logging OTP values)
- Webhook delivery monitoring configured
Summary
OTP SMS security is a layered problem — no single control is sufficient. Cryptographic generation, hash storage, short expiry, strict rate limiting, attempt locking, phishing-resistant templates, and SIM swap awareness must all be implemented together to build a resilient OTP system for Indian users.
Get Click Media delivers OTPs via a TRAI DLT-compliant transactional route with sub-3-second delivery, webhook receipts, and full DLT registration support — giving you a secure foundation to build on.
Frequently Asked Questions
SMS OTP is accepted by RBI as a valid second factor for banking transactions in India, but it is not immune to threats like SIM swap fraud and SS7 attacks. For high-value transactions, banks should combine SMS OTP with device binding, behavioural analytics, or app-based TOTP as an additional layer.
OTP bombing is an attack where a bad actor triggers hundreds of OTP requests to a victim's number, either as harassment or as a smokescreen to cover a fraudulent transaction. Prevent it by implementing strict rate limiting (e.g., max 5 OTP requests per number per hour), CAPTCHA on the request form, and alerting on abnormal OTP generation spikes.
In a SIM swap attack, a fraudster convinces a telecom operator to transfer the victim's number to a new SIM card. All incoming SMS — including OTPs — then go to the attacker's device. Mitigate this by monitoring for recent SIM changes via telecom APIs, enforcing cooling periods after SIM swap events, and offering app-based OTP (TOTP) as an alternative.
Industry best practice and RBI guidelines recommend OTP validity windows of 10 minutes or less for financial transactions. Shorter windows (3–5 minutes) reduce the risk of interception. Always invalidate the OTP immediately after a single successful use — never allow reuse.




