---
title: OTPs Must Have 20-bit Entropy Minimum
impact: MEDIUM
impactDescription: prevents guessing and brute-force attacks on One-Time Passwords
tags: otp, entropy, authentication, 2fa, security, php
---

## OTPs Must Have 20-bit Entropy Minimum

One-Time Passwords (OTPs) with low entropy, such as 4-digit codes, are susceptible to brute-force attacks. A 6-digit numeric OTP provides roughly 20 bits of entropy (1,000,000 combinations), which is the industry standard when combined with rate limiting.

**Incorrect (low entropy or predictable OTPs):**

```php
// 1. Weak - 4 digits (only 10,000 combinations)
$otp = rand(1000, 9999);

// 2. Predictable - using non-CS PRNG
$otp = substr(mt_rand(), 0, 6);

// 3. Very Weak - based on time
$otp = substr(time(), -6);
```

**Correct (CSPRNG generated numeric OTPs):**

```php
// 1. 6-digit OTP (Recommended minimum)
$otp = (string)random_int(100000, 999999);

// 2. 8-digit OTP (Extra security)
$otp = (string)random_int(10000000, 99999999);

// 3. Ensuring Leading Zeros (if needed)
$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
```

**Requirements for Secure OTPs:**
- **Generation**: Always use **`random_int()`**; never use `rand()` or `mt_rand()`.
- **Length**: Minimum **6 digits** for general use.
- **Single Use**: The code must be invalidated immediately after the first use (success or failure).
- **Rate Limiting**: Strictly limit the number of verification attempts (e.g., 3-5 attempts) before destroying the code (see rule **S045**).
- **Expiry**: Codes should expire within **5-10 minutes**.

**Tools:** PHP Internal `random_int()`, Laravel RateLimiter, Security Audit
