ASVS 5.0 V6.6.1 says phone and SMS delivered one time passwords may be offered only when the phone number was previously validated, a stronger method such as TOTP is also offered, and users are told about the security risks. At Level 3, phone and SMS must not be available at all. The requirement is Level 2.
Why it matters
SMS codes can be taken through SIM swapping, number porting fraud, and interception. They also create cost abuse, where bots trigger texts to premium numbers. They are better than nothing but weaker than app or key based factors.
How to implement it
- Validate the number at enrollment by sending a code and requiring it back before the number is used for authentication.
- Offer TOTP and WebAuthn next to SMS and default new users to the stronger ones.
- Show a short plain notice: SMS can be intercepted, an authenticator app is safer.
- Gate by assurance level. For Level 3 products, remove phone and SMS delivery entirely.
def enroll_phone(user, number, code):
if not verify_enrollment_code(user.id, number, code):
raise BadRequest()
db.phones.insert(user_id=user.id, number=number, verified_at=now())
def available_methods(user, level):
methods = ["totp", "webauthn"]
if level < 3 and user.has_verified_phone():
methods.append("sms")
return methods
How to test it
- Try to use an unverified number for login codes. It must fail.
- Confirm TOTP or a key is offered during setup.
- For a Level 3 build, confirm no SMS option exists in the UI or API.
Common mistakes
- Using an unverified number captured at signup.
- Making SMS the only option.
- Leaving SMS as a fallback for stronger factors.
Quick check
Can a Level 3 app keep SMS as a backup?
No. At Level 3 the requirement is that phone and SMS are not available as options.