When I was reviewing an application recently, I found a small piece of code that looked harmless at first. It was generating a six-digit OTP using C#’s Random() class.
The code worked. It generated a number. Everything looked fine.
But here’s the thing: it was not secure at all.
In login systems, password resets, or any feature where users depend on OTPs, the security of that number matters more than anything. So let’s break down the right way and wrong way to generate OTPs in C#.

What Most Developers Do (But Shouldn’t)
A lot of people use this:
public int GenerateOTP()
{
Random rnd = new Random();
return rnd.Next(100000, 999999);
}
This looks simple, clean, and effective. But here’s the real problem:
✔ It’s NOT secure
✔ It’s predictable
✔ Attackers can guess the next OTP
C#’s Random() uses time-based seeds. If someone knows the approximate time your OTP was generated, they can recreate the same values. Even worse, if two OTPs generate within milliseconds, they might be identical.
This is why banks, fintech apps, and secure platforms never use Random() for OTPs.
The Right Way: RandomNumberGenerator (Secure)
Here’s how a secure OTP should be generated:
public int GenerateOTP()
{
byte[] bytes = new byte[4];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(bytes);
int num = Math.Abs(BitConverter.ToInt32(bytes, 0)) % 900000 + 100000;
return num;
}
Why this is better:
It is cryptographically secure
The OTP is not predictable
Attackers cannot reproduce or guess it
It follows the same principles used by banks, OTP services, and big tech companies
You get a truly random, secure six-digit code every time.
Why OTP Security Matters
Think of an OTP like a temporary password. If someone can guess it, predict it, or generate it through timing, then:
Accounts can be hacked
Password resets can be hijacked
Payment flows can be compromised
An OTP is often the last line of defense, so its randomness must be strong.

Join the conversation! Your thoughts help the community grow.