Small Space of Random Values
Description
Small Space of Random Values is a vulnerability that occurs when the number of possible random values that a product can generate is smaller than needed for security, making the system susceptible to brute force attacks. Even when using a cryptographically secure random number generator, if the output is constrained to a small space (such as short strings, limited character sets, or truncated values), attackers can enumerate all possibilities in a feasible time frame. This effectively reduces the security of the system to the number of possible values rather than the theoretical strength of the random source. Examples include short session IDs, limited-length tokens, small numeric ranges for verification codes, and file names with insufficient uniqueness.
Risk
A small random value space allows attackers to brute force through all possible values in practical time. If session IDs are only 32 bits (about 4 billion values), an attacker making 10,000 requests per second can enumerate all possibilities in roughly 5 days. 16-bit values (65,536 possibilities) can be brute-forced in seconds. The risk is particularly severe for session management, password reset tokens, and authentication codes where successful guessing grants access. Attackers can parallelize brute force attempts across multiple machines, further reducing attack time. The vulnerability is often introduced when developers prioritize usability (shorter codes, memorable values) over security, or when legacy system constraints limit value length.
Solution
Ensure random values have sufficient size for their security context. Session identifiers should be at least 128 bits (16 bytes) as recommended by OWASP. Cryptographic keys should be at least 128 bits for symmetric encryption and 2048 bits for RSA. Use FIPS 140-2 or 140-3 compliant random number generators with adequate output sizes. Calculate the required value space based on the expected attack rate and desired security lifetime: for a value that should resist 10 million guesses per day for one year, you need at least 52 bits (10M × 365 = ~3.6 billion attempts). Avoid truncating random values after generation. Use URL-safe base64 encoding to maximize entropy per character when length is constrained.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Attackers can brute force through the small value space to guess valid session IDs, tokens, or identifiers, bypassing authentication and authorization controls. |
| Confidentiality | Scope: Confidentiality Small cryptographic key spaces or nonces can be brute-forced, compromising the confidentiality of encrypted data. |
| Integrity | Scope: Integrity Predictable file names or resource identifiers with small value spaces allow attackers to access or overwrite resources belonging to other users. |
Example Code
Vulnerable Code (Java/Python)
The following examples demonstrate small random value space vulnerabilities:
// Vulnerable: Small space of random values in Java
import java.util.Random;
public class VulnerableSmallSpace {
// Vulnerable: Only 8-byte session ID (too short)
public String vulnerableSessionId() {
Random rand = new Random();
// Vulnerable: 8 hex chars = 32 bits = 4 billion possibilities
return String.format("%08x", rand.nextInt());
}
// Vulnerable: 5-character alphanumeric code
public String vulnerableVerificationCode() {
// Vulnerable: Only 5 chars from 36-char alphabet
// 36^5 = 60,466,176 possibilities - brute forced in minutes
String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder();
Random rand = new Random();
for (int i = 0; i < 5; i++) {
sb.append(chars.charAt(rand.nextInt(36)));
}
return sb.toString();
}
// Vulnerable: 4-digit PIN
public String vulnerablePin() {
// Vulnerable: Only 10,000 possibilities
return String.format("%04d", new Random().nextInt(10000));
}
// Vulnerable: Short file names
public String vulnerableFileName() {
// Vulnerable: 6-char hex = 16 million possibilities
return String.format("%06x", new Random().nextInt(0xFFFFFF));
}
// Vulnerable: Using small subset of UUID
public String vulnerableShortUuid() {
// Vulnerable: Truncating UUID defeats its purpose
String uuid = java.util.UUID.randomUUID().toString();
return uuid.substring(0, 8); // Only 32 bits!
}
}
# Vulnerable: Small space of random values in Python
import random
import string
# Vulnerable: Short session ID
def vulnerable_session_id():
# Vulnerable: Only 8 hex characters = 32 bits
return format(random.randint(0, 0xFFFFFFFF), '08x')
# Vulnerable: Limited character verification code
def vulnerable_verification_code():
# Vulnerable: 6-digit numeric code
# Only 1 million possibilities
return format(random.randint(0, 999999), '06d')
# Vulnerable: Short temporary password
def vulnerable_temp_password():
# Vulnerable: 6 chars from limited set
# 26^6 = 308 million - sounds big but attackable
chars = string.ascii_lowercase
return ''.join(random.choice(chars) for _ in range(6))
# Vulnerable: Small random file identifier
def vulnerable_file_id():
# Vulnerable: Only 10000 possible values
return f"file_{random.randint(0, 9999)}.tmp"
# Vulnerable: Truncated token
def vulnerable_api_token():
import secrets
# Vulnerable: Generating secure random but truncating!
full_token = secrets.token_hex(32)
return full_token[:8] # Defeats the purpose
# Vulnerable: Small numeric range
def vulnerable_order_id():
# Vulnerable: Sequential-ish with small random component
base = 1000000
random_part = random.randint(0, 999) # Only 1000 values!
return base + random_part
// Vulnerable: Small space of random values in C
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
// Vulnerable: 16-bit session ID
unsigned short vulnerable_session_id() {
// Vulnerable: Only 65536 possibilities
return rand() % 65536;
}
// Vulnerable: 4-character code
void vulnerable_verification_code(char *code) {
// Vulnerable: 4 alphanumeric = 36^4 = 1.6 million
const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < 4; i++) {
code[i] = chars[rand() % 36];
}
code[4] = '\0';
}
// Vulnerable: Small file ID range
int vulnerable_temp_file_id() {
// Vulnerable: Only 1000 values
return rand() % 1000;
}
// Vulnerable: 3-byte random value
void vulnerable_token(unsigned char *token) {
// Vulnerable: 24 bits = 16 million possibilities
token[0] = rand() % 256;
token[1] = rand() % 256;
token[2] = rand() % 256;
}
// Vulnerable: Small salt for hashing
void vulnerable_salt(unsigned char *salt) {
// Vulnerable: 2-byte salt = 65536 values
// Rainbow tables become feasible
salt[0] = rand() % 256;
salt[1] = rand() % 256;
}
Fixed Code (Java/Python)
// Fixed: Adequate random value space in Java
import java.security.SecureRandom;
import java.util.Base64;
public class SecureValueSpace {
private SecureRandom secureRandom = new SecureRandom();
// Fixed: 128-bit session ID (minimum recommended)
public String secureSessionId() {
// Fixed: 16 bytes = 128 bits = 3.4 × 10^38 possibilities
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// Fixed: Secure verification code (when short code required)
public String secureVerificationCode() {
// If short code is needed for usability, combine with:
// - Rate limiting (max 5 attempts)
// - Short expiration (10 minutes)
// - Account lockout
// Still use reasonable size: 6-digit with rate limiting
// Or 8-character alphanumeric for better security
byte[] bytes = new byte[6];
secureRandom.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding()
.encodeToString(bytes).substring(0, 8);
}
// Fixed: Secure temporary password
public String secureTempPassword() {
// Fixed: 32 bytes = 256 bits of entropy
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
// Fixed: Secure file identifier
public String secureFileId() {
// Fixed: 128 bits ensures uniqueness
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return bytesToHex(bytes);
}
// Fixed: Full-length API token
public String secureApiToken() {
// Fixed: 256 bits for API tokens
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytesToHex(bytes);
}
// Fixed: Secure order reference
public String secureOrderId() {
// Fixed: Use UUID which provides 122 random bits
return java.util.UUID.randomUUID().toString();
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
# Fixed: Adequate random value space in Python
import secrets
import os
# Fixed: 128-bit session ID
def secure_session_id():
# Fixed: 32 hex chars = 128 bits
return secrets.token_hex(16)
# Fixed: Secure verification code
def secure_verification_code():
# Fixed: For short codes, combine with security controls:
# - Rate limiting
# - Short TTL (5-10 minutes)
# - Account lockout after N attempts
# If numeric required, use 8 digits minimum
return format(secrets.randbelow(100000000), '08d')
# Fixed: Secure temporary password
def secure_temp_password():
# Fixed: 256 bits URL-safe
return secrets.token_urlsafe(32)
# Fixed: Secure file identifier
def secure_file_id():
# Fixed: 128 bits ensures collision resistance
return secrets.token_hex(16)
# Fixed: Full-length API token
def secure_api_token():
# Fixed: 256 bits for high-security tokens
return secrets.token_hex(32)
# Fixed: Secure order reference
import uuid
def secure_order_id():
# Fixed: UUID4 provides 122 random bits
return str(uuid.uuid4())
# Fixed: Cryptographic salt
def secure_salt():
# Fixed: 16 bytes (128 bits) minimum for salts
return os.urandom(16)
# Fixed: Calculate required entropy for given security level
def calculate_required_bits(attempts_per_second, security_years,
success_probability=1e-6):
"""
Calculate bits needed for given security parameters.
Example: 10000 attempts/sec, 1 year protection, 1-in-million success
requires about 52 bits minimum.
"""
total_attempts = attempts_per_second * 86400 * 365 * security_years
required_space = total_attempts / success_probability
import math
return math.ceil(math.log2(required_space))
// Fixed: Adequate random value space in C
#include <openssl/rand.h>
#include <stdio.h>
#include <string.h>
// Fixed: 128-bit session ID
int secure_session_id(char *session_id, size_t max_len) {
unsigned char bytes[16]; // 128 bits
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
return -1;
}
// Convert to hex (32 chars + null)
if (max_len < 33) return -1;
for (int i = 0; i < 16; i++) {
sprintf(session_id + (i * 2), "%02x", bytes[i]);
}
return 0;
}
// Fixed: Secure verification code
int secure_verification_code(char *code, size_t max_len) {
// Fixed: 8-byte random for code generation
unsigned char bytes[8];
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
return -1;
}
// Convert to alphanumeric (reasonable length)
const char *chars = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz";
int char_count = strlen(chars);
for (int i = 0; i < 8 && i < (int)max_len - 1; i++) {
code[i] = chars[bytes[i] % char_count];
}
code[8] = '\0';
return 0;
}
// Fixed: Secure file identifier
int secure_file_id(char *file_id, size_t max_len) {
unsigned char bytes[16]; // 128 bits
if (RAND_bytes(bytes, sizeof(bytes)) != 1) {
return -1;
}
if (max_len < 33) return -1;
for (int i = 0; i < 16; i++) {
sprintf(file_id + (i * 2), "%02x", bytes[i]);
}
return 0;
}
// Fixed: Secure salt generation
int secure_salt(unsigned char *salt, size_t len) {
// Fixed: At least 16 bytes for salts
if (len < 16) {
return -1; // Enforce minimum size
}
return RAND_bytes(salt, len) == 1 ? 0 : -1;
}
The fix uses sufficient value spaces: 128 bits minimum for identifiers, 256 bits for cryptographic tokens.
Exploited in the Wild
Short Session ID Hijacking (Various)
Multiple web applications with 32-bit session IDs have been exploited through brute force attacks, allowing attackers to hijack active sessions.
SYN Cookie Weakness (CVE-2003-1230)
A SYN cookies implementation limited to 32-bit keys allowed attackers to brute force through all possible values.
Tools to Test/Exploit
-
Burp Suite Intruder — Can enumerate small value spaces through automated requests.
-
Hydra — Network login cracker that can brute force authentication codes.
-
Custom scripts — Simple scripts can enumerate small spaces in minutes.
CVE Examples
-
CVE-2002-0583 — Only 5 alphanumeric characters for filenames.
-
CVE-2003-1230 — SYN cookies limited to 32-bit keys.
References
-
MITRE Corporation. "CWE-334: Small Space of Random Values." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/334.html
-
OWASP Foundation. "Session Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
-
NIST. "FIPS 140-2: Security Requirements for Cryptographic Modules." https://csrc.nist.gov/publications/detail/fips/140/2/final