Blog | G5 Cyber Security

AES256 & Math.random: Security Risks

TL;DR

Using Math.random as a source of randomness for AES256 encryption is extremely insecure and easily crackable. This guide explains why, and how to avoid the problem.

Why Math.random is Bad for Encryption

AES256 requires strong, unpredictable random numbers (a cryptographic-quality random number generator) for its keys and initialization vectors (IVs). Math.random isn’t one of them. Here’s why:

How an Attack Works

An attacker can exploit the weaknesses of Math.random in several ways:

  1. Seed Recovery: If the seed used by Math.random is known or can be guessed (e.g., based on timing information), the entire key stream can be reconstructed.
  2. State Reconstruction: Even without knowing the exact seed, an attacker might be able to reconstruct the internal state of the random number generator after observing a sufficient number of outputs.
  3. Brute Force (Reduced Key Space): The limited randomness significantly reduces the effective key space, making brute-force attacks feasible.

Step-by-Step: Demonstrating the Problem

This example shows how easily a key generated with Math.random can be compromised (simplified for illustration). Do not use this code in production!

  1. Generate a Key (Insecurely):
  2. const crypto = require('crypto');
    const keyLength = 32; // AES256 requires 32 bytes
    let insecureKey = '';
    for (let i = 0; i < keyLength; i++) {
      insecureKey += Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
    }
    console.log('Insecure Key:', insecureKey);
    
  3. Encrypt Data:
  4. const cipher = crypto.createCipheriv('aes-256-cbc', insecureKey, Buffer.from('initialization_vector'));
    let encryptedData = cipher.update(Buffer.from('Sensitive data')).toString('hex');
    encryptedData += cipher.final('hex');
    console.log('Encrypted Data:', encryptedData);
    
  5. Attempt to Decrypt (with a guessed seed):
  6. // In reality, this would involve more sophisticated techniques to recover the seed.
    const guessedSeed = 12345; // Example guess
    let keyFromSeed = '';
    for (let i = 0; i < keyLength; i++) {
        keyFromSeed += Math.floor((Math.random() * 256) % 256).toString(16).padStart(2, '0'); //Recreate the key based on seed.
    }
    
    const decipher = crypto.createDecipheriv('aes-256-cbc', keyFromSeed, Buffer.from('initialization_vector'));
    let decryptedData = decipher.update(Buffer.from(encryptedData, 'hex')).toString('utf8');
    decryptedData += decipher.final('utf8');
    console.log('Decrypted Data:', decryptedData);
    

    With a correct or close-enough seed guess (or through more advanced techniques), the decryption will succeed, revealing the sensitive data.

How to Generate Secure Random Numbers

  1. Use Cryptographically Secure APIs: Use your operating system’s built-in random number generators.
  2. Node.js: The crypto module provides secure random number generation.
  3. const crypto = require('crypto');
    const keyLength = 32;
    const key = crypto.randomBytes(keyLength);
    console.log('Secure Key:', key.toString('hex'));
    
  4. Browser: Use the window.crypto.getRandomValues() method.
  5. const array = new Uint8Array(32);
    window.crypto.getRandomValues(array);
    console.log('Secure Key:', Array.from(array).map((x) => x.toString(16)).join(''));
    
  6. Initialization Vector (IV): Always generate a unique, random IV for each encryption operation.

Important Considerations

Exit mobile version