Generate Secure OTP & Validate Safely in C#

One-time passwords (OTPs) are widely used for authentication in login flows, password resets, banking transactions, and multi-factor authentication (MFA). However, insecure OTP generation or improper validation can expose your system to brute-force attacks, replay attacks, and other security risks.

This post explains how to generate cryptographically secure OTPs in C# and how to validate them safely, following modern .NET security standards.

How to Generate Secure OTP & Validate Safely in C#

Getting Started

One-time passwords (OTPs) have become a core security component in modern applications. Whether you're building login flows, password reset systems, banking apps, or multi-factor authentication (MFA) features. Users expect secure, fast, and reliable OTP delivery. Developers, on the other hand, must ensure that these OTPs cannot be predicted, intercepted, reused, or brute-forced.

While generating an OTP might look simple at first glance (just pick a few random digits) but the reality is that poor OTP implementations are one of the most common security gaps in authentication systems. Many developers unknowingly use insecure random number generators, store OTPs in plain text, or validate them in ways that leak timing information, making their system vulnerable to attack.

Many beginners generate OTPs using:

var random = new Random();
int otp = random.Next(100000, 999999);

This is not secure, because:
  • Random() is not cryptographically secure
  • Outputs can be predicted based on the seed
  • Attackers can brute-force OTPs more easily
To generate secure OTPs, use RandomNumberGenerator in System.Security.Cryptography.

Generate Cryptographically Secure OTP

In .NET, the right tools exist to generate and handle OTPs securely, but they must be used correctly. This post explores how to generate cryptographically secure OTPs in C# using modern APIs, how to store and validate them safely, and what practical best practices you should follow to protect your users and your application. Whether you're building a simple 6-digit verification code or a more advanced alphanumeric code, following these guidelines will dramatically improve the security of your authentication process.

Numeric OTP (most common)
using System.Security.Cryptography;
public static string GenerateNumericOtp(int length = 6)
{
  var bytes = new byte[length];
  RandomNumberGenerator.Fill(bytes);
  // Convert to digits 0–9
  char[] result = new char[length];
  for (int i = 0; i < length; i++)
  {
    result[i] = (bytes[i] % 10).ToString()[0];
  }
  return new string(result);
}
Why this is secure:
  • RandomNumberGenerator uses strong cryptographic randomness
  • Output is unpredictable
  • Works across all .NET versions
Alphanumeric OTP (more secure)
public static string GenerateAlphaNumericOtp(int length = 8)
{
  const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  var bytes = new byte[length];
  RandomNumberGenerator.Fill(bytes);
  char[] result = new char[length];
  for (int i = 0; i < length; i++)
  {
    result[i] = chars[bytes[i] % chars.Length];
  }
  return new string(result);
}
Alphanumeric OTPs offer significantly more entropy.

Storing OTP Securely

Never store OTP in plain text. Instead, store a hashed OTP, just like passwords.

Example:-Using SHA-256 hashing:
public static string HashOtp(string otp)
{
  using var sha = SHA256.Create();
  var hash = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(otp));
  return Convert.ToHexString(hash);
}
Store in database Like:
UserId OTP_Hash ExpiresAt Attempts
1 482915 5 3

Validating OTP Safely

using System.Security.Cryptography;
public static bool ValidateOtp(
string inputOtp,
string storedHash,
DateTime expiry,
ref int attempts,
int maxAttempts = 5)
{
  if (DateTime.UtcNow > expiry)
  return false;
  if (attempts >= maxAttempts)
  return false;
  attempts++;
  string hashedInput = HashOtp(inputOtp);
  return CryptographicOperations.FixedTimeEquals(
  Convert.FromHexString(hashedInput),
  Convert.FromHexString(storedHash)
  );
}
Validation requires:
  • Hashing input OTP
  • Constant-time comparison
  • Expiry check
  • Attempt-limit check
Security benefits:
  • Prevents timing attacks
  • Blocks infinite brute force attempts
  • Ensures OTP cannot be reused after expiry
Recommended OTP Expiry & Attempt Limits
Usage Scenario Recommended Expiry Max Attempts
Login MFA 2–5 minutes 3–5
High-risk transactions 1–2 minutes 1–3
Password reset 5–10 minutes 5

Protecting OTP Delivery

Even with secure generation, OTP can be compromised during transmission. Best practices:
  • Use HTTPS/TLS for API communication
  • For SMS, use trusted providers (Twilio, AWS SNS, etc.)
  • For email, enable SPF + DKIM + DMARC
  • Mask OTP in logs (never log plain OTP)

Summary

Generating secure OTPs in C# is straightforward when using the correct cryptographic APIs. Follow and implementing these guidelines, you protect your authentication system against brute-force, replay, and prediction attacks.

Thanks

Kailash Chandra Behera

I am an IT professional with over 13 years of experience in the full software development life cycle for Windows, services, and web-based applications using Microsoft .NET technologies.

Previous Post Next Post

نموذج الاتصال