Skip to main content

Insecure Use of Cryptography

Fixing Insecure Use of Pseudo Random Number Generator

About insecure randomness

What is insecure randomness?

Random number generation is the process of generating a sequence of numbers or symbols that cannot be reasonably predicted better than by random chance. The ability to generate true random numbers is important in many fields, including cryptography, simulation, and gaming, where the results must be unpredictable and unbiased.

In computer science and cryptography, random number generation is used to generate cryptographic keys, secure passwords, and other security-related data. Pseudorandom number generators (PRNGs) are commonly used to generate random numbers in computer systems, but they are not truly random since their output is determined by an algorithm that uses a seed value to generate a sequence of numbers. True random number generators (TRNGs) use physical processes or natural sources of randomness, such as radioactive decay or atmospheric noise, to generate true random numbers.

The quality of random number generation is crucial in many applications, particularly in the field of cryptography. If the random numbers are predictable or biased, they can be used to compromise the security of the system. Therefore, it is important to use reliable and secure random number generators in security-critical applications.

In summary, random number generation is the process of generating a sequence of numbers or symbols that cannot be reasonably predicted better than by random chance. True random number generators are preferable for security-critical applications to ensure that the output is unbiased and unpredictable.

Check out this video for a high-level explanation:

What is the impact of insecure randomness?

Insecure randomness in security systems has significant impacts on the security and privacy of data. Here are some of the potential impacts:

  • Weak cryptography: Insecure randomness can result in the use of weak cryptographic keys or other security parameters, which can be exploited by attackers to gain unauthorized access to sensitive data. This can result in data breaches, where sensitive data is stolen or leaked.
  • Malicious attacks: Attackers can use insecure randomness to launch various types of attacks, such as brute-force attacks or other guessing attacks, which can be used to break weak or outdated security systems.
  • Regulatory compliance issues: Insecure randomness can result in non-compliance with various security standards and regulations, which can result in financial penalties or other legal consequences.

How to prevent insecure randomness?

Several measures can prevent insecure randomness, including:

  • Use reliable and secure random number generators: Use reliable and secure random number generators that have been widely tested and validated by security experts. True random number generators (TRNGs) are preferable for security-critical applications to ensure that the output is unbiased and unpredictable.
  • Use appropriate cryptographic keys and security parameters: Use appropriate cryptographic keys and security parameters to ensure that the security system is strong enough to resist attacks. The strength of the cryptographic keys and security parameters should be based on the level of security required by the system.
  • Regularly update software and systems: Regularly update software and systems to ensure that the latest security patches are applied and known vulnerabilities related to insecure randomness are addressed.
  • Regularly review and update security policies and procedures: Regularly review and update security policies and procedures to ensure that they remain up-to-date with the latest best practices and standards.

References

Taxonomies

Explanation & Prevention

Training

The functions crypto.pseudoRandomBytes, Math.random and Math.randomInRange are not suitable for cryptographic purposes. If you obtain a certain amount of data from one of these sources, you can then predict the rest of the data it will generate.

Because of this, they shouldn't be used for random numbers that you don't want to be guessable, for example generating numbers for a TOTP 2 Factor Authentication app.

Rule-specific references:

Option A: Use a Secure Random Number Generator

If you require your generated data to be cryptographically secure, use a Cryptographically Secure Pseudo Random Number Generator (CSPRNG).

  1. Locate one of the following vulnerable patterns:

    Using crypto.pseudoRandomBytes

     // ...
    let receipts = {};

    app.get('/receipt/:id', function(req, res) {
    if(req.params.id in receipts) {
    res.json(receipts[req.params.id])
    } else {
    res.status(404).json({'error': 'not found'});
    }
    });

    app.post('/checkout', function(req, res) {
    let receipt = {
    items: req.body.itemNames,
    total: req.body.itemPrices.reduce((total, n) => total + (parseInt(n) || 0), 0)
    };

    let receiptId = crypto.pseudoRandomBytes(20).toString('hex');
    receipts[receiptId] = receipt;

    res.redirect(`/receipt/${receiptId}`);
    });

    Using Math.random

     // ...
    let receipts = {};

    app.get('/receipt/:id', function(req, res) {
    if(req.params.id in receipts) {
    res.json(receipts[req.params.id])
    } else {
    res.status(404).json({'error': 'not found'});
    }
    });

    app.post('/checkout', function(req, res) {
    let receipt = {
    items: req.body.itemNames,
    total: req.body.itemPrices.reduce((total, n) => total + (parseInt(n) || 0), 0)
    };

    let receiptId = Math.floor(Math.random() * 1_000_000_000_000_000_000_000)
    receipts[receiptId] = receipt;

    res.redirect(`/receipt/${receiptId}`);
    });
  2. Replace it with one of the following patterns:

    Fix with crypto.randomBytes(...)

     // ...
    let receipts = {};

    app.get('/receipt/:id', function(req, res) {
    if(req.params.id in receipts) {
    res.json(receipts[req.params.id])
    } else {
    res.status(404).json({'error': 'not found'});
    }
    });

    app.post('/checkout', function(req, res) {
    let receipt = {
    items: req.body.itemNames,
    total: req.body.itemPrices.reduce((total, n) => total + (parseInt(n) || 0), 0)
    };

    let receiptId = crypto.randomBytes(20).toString('hex');
    receipts[receiptId] = receipt;

    res.redirect(`/receipt/${receiptId}`);
    });

    Fix with crypto.randomInt(...)

     // ...
    let receipts = {};

    app.get('/receipt/:id', function(req, res) {
    if(req.params.id in receipts) {
    res.json(receipts[req.params.id])
    } else {
    res.status(404).json({'error': 'not found'});
    }
    });

    app.post('/checkout', function(req, res) {
    let receipt = {
    items: req.body.itemNames,
    total: req.body.itemPrices.reduce((total, n) => total + (parseInt(n) || 0), 0)
    };

    let receiptId = "";
    for(let i = 0; i < 30; i++) {
    receiptId += crypto.randomInt(10);
    }
    receipts[receiptId] = receipt;

    res.redirect(`/receipt/${receiptId}`);
    });

    Fix with crypto.randomUUID()

     // ...
    let receipts = {};

    app.get('/receipt/:id', function(req, res) {
    if(req.params.id in receipts) {
    res.json(receipts[req.params.id])
    } else {
    res.status(404).json({'error': 'not found'});
    }
    });

    app.post('/checkout', function(req, res) {
    let receipt = {
    items: req.body.itemNames,
    total: req.body.itemPrices.reduce((total, n) => total + (parseInt(n) || 0), 0)
    };

    let receiptId = crypto.randomUUID();
    receipts[receiptId] = receipt;

    res.redirect(`/receipt/${receiptId}`);
    });
  3. Test it

  4. Ship it 🚢 and relax 🌴

Fixing insecure algorithms and cipher modes

About insecure algorithms and cipher modes

What are insecure algorithms and cipher modes?

A cryptographic algorithm and a cipher mode are two different concepts used in cryptography.

A cryptographic algorithm is a mathematical function used to encrypt or decrypt data. It defines the rules for transforming plaintext (unencrypted) data into ciphertext (encrypted) data, and vice versa. Common cryptographic algorithms include Advanced Encryption Standard (AES), RSA, and Triple Data Encryption Standard (3DES).

On the other hand, a cipher mode is a method of applying a cryptographic algorithm to encrypt or decrypt data. It defines the way in which plaintext is broken into blocks and how these blocks are transformed into ciphertext. Common cipher modes include Electronic Codebook (ECB), Cipher Block Chaining (CBC), and Galois/Counter Mode (GCM).

The difference between a cryptographic algorithm and a cipher mode is that an algorithm defines the mathematical rules for encryption and decryption, while a cipher mode defines the specific way in which these rules are applied to transform plaintext into ciphertext.

A cryptographic algorithm is a fundamental building block of cryptography, while a cipher mode provides additional security features and determines how data is processed.

Insecure algorithms are cryptographic algorithms that are known to have vulnerabilities that can be exploited by attackers. Cryptographic algorithms are used in security systems to protect data.

An example of an insecure algorithm is the Data Encryption Standard (DES), which is vulnerable to brute-force attacks.

Insecure cipher modes are cryptographic modes that have vulnerabilities or weaknesses that can be exploited by attackers to compromise the security of the encryption. The use of insecure cipher modes can result in data being decrypted or tampered with by unauthorized parties, which can lead to serious security breaches and data leaks.

Some examples of insecure cipher modes include:

  • Electronic Codebook (ECB): ECB mode is insecure because it encrypts each block of plaintext independently, which can lead to patterns in the ciphertext that reveal information about the plaintext.
  • Cipher Block Chaining (CBC) with a static IV: CBC mode with a static initialization vector (IV) is vulnerable to chosen plaintext attacks, where an attacker can manipulate the plaintext and observe the resulting ciphertext to learn more about the encryption algorithm.
  • Cipher Feedback (CFB) mode with a small segment size: CFB mode with a small segment size can be vulnerable to bit-flipping attacks, where an attacker can manipulate the ciphertext to change the decrypted plaintext.
  • Stream cipher modes using weak key schedules: Some stream cipher modes use weak key schedules that can be easily broken by attackers, allowing them to decrypt the ciphertext and gain access to sensitive data.

Check out this video for a high-level explanation:

What is the impact of insecure algorithms and cipher modes?

Insecure algorithms in security systems can have significant impacts on the security and privacy of data.

Here are some of the potential impacts:

  • Data breaches: Insecure algorithms and cipher modes can be exploited by attackers to decrypt or tamper with encrypted data, leading to data breaches and the exposure of sensitive information.
  • Data loss: In some cases, the use of insecure algorithms and cipher modes can result in the loss of encrypted data, either through accidental deletion or malicious tampering by attackers.
  • Compliance violations: The use of insecure algorithms and cipher modes can lead to compliance violations with industry standards and regulations, such as the Payment Card Industry Data Security Standard (PCI DSS) or the General Data Protection Regulation (GDPR).
  • Reputation damage: In the event of a data breach or other security incident caused by insecure algorithms and cipher modes, organizations may suffer reputational damage, loss of customer trust, and legal or financial penalties.

How to prevent insecure algorithms and cipher modes?

Several measures can prevent the use of insecure algorithms, including:

  • Use strong cryptographic algorithms: Use strong and up-to-date cryptographic algorithms that have been widely tested and validated by security experts. For example, Advanced Encryption Standard (AES) encryption algorithm is widely used and has been proven to be secure.
  • Disable or remove insecure algorithms: Disable or remove insecure algorithms, such as DES or RC4, from systems and applications.
  • Use well-designed cipher modes: Use well-designed cipher modes that provide strong security guarantees, such as Cipher Block Chaining (CBC) with randomized initialization vectors (IVs) or Galois/Counter Mode (GCM).
  • Avoid using weak cipher modes: Avoid using insecure cipher modes such as Electronic Codebook (ECB) or Cipher Feedback (CFB) mode with a small segment size.
  • Regularly update cryptographic libraries and dependencies: Keep all cryptographic libraries and dependencies up-to-date with the latest security patches and updates.
  • Regularly review and update security policies and procedures: Regularly review and update security policies and procedures to ensure that they remain up-to-date with the latest best practices and standards.

References

Taxonomies

Explanation & Prevention

Training

Weak encryption algorithms

This rule detects the use of the following known weak encryption (or mode of encryption) algorithms:

It is recommended to use AES-GCM (or any AES mode of operation other than ECB), Chacha20-Poly1305, instead of the encryption algorithms above.

Option A: Use a secure encryption algorithm (CJS)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createCipheriv, randomBytes } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes} = require('crypto');

    const algorithm = 'aes-256-ecb';
    const key = randomBytes(32);

    // Insecure example
    const cipher = createCipheriv(algorithm, key, null);
  2. Replace weak encryption algorithm with AES-GCM.

    const { createCipheriv, randomBytes } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes } = require('crypto');

    const algorithm = 'aes-256-gcm';
    const key = randomBytes(32);
    const iv = randomBytes(12);

    // Secure example
    const cipher = createCipheriv(algorithm, key, iv);
  3. Test it

  4. Ship it 🚢 and relax 🌴

Option B: Use a secure encryption algorithm (ESM)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createCipheriv, randomBytes } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes } = await import('crypto');

    const algorithm = 'aes-256-ecb';
    const key = randomBytes(32);

    // Insecure example
    const cipher = createCipheriv(algorithm, key, null);
  2. Replace weak encryption algorithm with AES-GCM.

    const { createCipheriv, randomBytes } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes } = await import('crypto');

    const algorithm = 'aes-256-gcm';
    const key = randomBytes(32);
    const iv = randomBytes(12);

    // Secure example
    const cipher = createCipheriv(algorithm, key, iv);
  3. Test it

  4. Ship it 🚢 and relax 🌴

Insecure use of createCipher

This rule detects the use of createCipher and createDecipher in NodeJS applications. Both functions are deprecated as of NodeJS version 10, see the documentation on createCipher and the documentation on createDecipher for more information.

Both functions are deprecated because the way of generating the Initialization Vector used by createCipher and createDecipher are fatally flawed.

It is recommended to use createCipheriv and createDecipheriv.

Option A: Use the secure version (CJS)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createCipher, randomBytes } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipher, randomBytes} = require('crypto');

    const algorithm = 'aes-256-cbc';
    const key = randomBytes(32);

    // Insecure example
    const cipher = createCipher(algorithm, key);
  2. Replace the createCipher or createDecipher functions with createCipheriv or createDecipheriv

    const { createCipheriv, randomBytes } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes } = require('crypto');

    const algorithm = 'aes-256-cbc';
    const key = randomBytes(32);
    const iv = randomBytes(16);

    // Secure example
    const cipher = createCipheriv(algorithm, key, iv);
  3. Test it

  4. Ship it 🚢 and relax 🌴

Option B: Use the secure version (ESM)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createCipher, randomBytes } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipher, randomBytes } = await import('crypto');

    const algorithm = 'aes-256-cbc';
    const key = randomBytes(32);

    // Insecure example
    const cipher = createCipher(algorithm, key);
  2. Replace the createCipher or createDecipher functions with createCipheriv or createDecipheriv

    const { createCipheriv, randomBytes } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createCipheriv, randomBytes } = await import('crypto');

    const algorithm = 'aes-256-cbc';
    const key = randomBytes(32);
    const iv = randomBytes(16);

    // Secure example
    const cipher = createCipheriv(algorithm, key, iv);
  3. Test it

  4. Ship it 🚢 and relax 🌴

Fixing Insecure Hashes

About Insecure Hashes

What are insecure hashes?

Insecure hashes are cryptographic hash functions that are vulnerable to attacks that can compromise the integrity and authenticity of data. Cryptographic hashes are widely used in security systems to ensure the integrity of data, such as passwords or digital signatures, by generating a fixed-length output that represents the original data.

Insecure hashes can be exploited by attackers to manipulate the original data without being detected, resulting in significant security vulnerabilities.

Examples of insecure hashes include the Message Digest 5 (MD5) and Secure Hash Algorithm 1 (SHA-1), which are vulnerable to collision attacks. A collision attack is an attack where an attacker can generate two different pieces of data that have the same hash value, which can be used to substitute one piece of data for another, without being detected.

Check out this video for a high-level explanation:

What is the impact of insecure hashes?

Insecure hashes in security systems have significant impacts on the security and privacy of data. Here are some of the potential impacts:

  • Data breaches: Insecure hashes can result in vulnerabilities that can be exploited by attackers to gain unauthorized access to sensitive data.
  • Information disclosure: Insecure hashes can also result in vulnerabilities that allow attackers to manipulate and forge data, which can result in information disclosure and impersonation.
  • Malicious attacks: Attackers can use insecure hashes to launch various types of attacks, such as collision attacks or dictionary attacks, which can be used to break weak or outdated hashes.
  • Reduced trust: Insecure hashes can erode the trust that users and customers have in a system or application. This can result in reputational damage and financial losses.

How to prevent insecure hashes?

Several measures can prevent the use of insecure hashes, including:

  • Use strong cryptographic hash functions: Use strong and up-to-date cryptographic hash functions that have been widely tested and validated by security experts, such as SHA-256 or SHA-3. Avoid using outdated hash functions like MD5 or SHA-1, which are known to be insecure.
  • Use appropriate hash lengths: Use appropriate hash lengths to ensure that the cryptographic hashes generated are strong enough to resist attacks. Longer hash lengths are generally more secure and harder to break.
  • Use salt values: Use salt values to further strengthen the security of the cryptographic hashes generated. Salt values are random data that are added to the original data before hashing, which makes it harder for attackers to use precomputed tables or dictionaries to break the hashes.
  • Regularly update software and systems: Regularly update software and systems to ensure that the latest security patches are applied and known vulnerabilities related to insecure hashes are addressed.
  • Regularly review and update security policies and procedures: Regularly review and update security policies and procedures to ensure that they remain up-to-date with the latest best practices and standards.

References

Taxonomies

Explanation & Prevention

Training

This rule detects usages of the following known weak hash algorithms:

  • MD4 and MD5: These functions have known vulnerabilities and are considered deprecated.
  • SHA-1: SHA-1 is not collision resistant and is therefore not suitable as a cryptographic signature.

It detects these weak hash algorithms for both CommonJS (CJS) and ESM (modules) for the NodeJS crypto module.

It is recommended to use SHA3-256, or any other stronger alternatives, instead of MD4, MD5 or SHA-1.

Option A: Use a strong hash algorithm (CJS)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createHash } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createHash } = require('crypto');

    // Insecure example
    const hash = createHash('md4');
  2. Replace weak hash algorithm with SHA3-256.

    const { createHash } = require('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createHash } = require('crypto');

    // Secure example
    const hash = createHash('SHA3-256');
  3. Test it

  4. Ship it 🚢 and relax 🌴

Option B: Use a strong hash algorithm (ESM)

  1. Go through the issues that GuardRails identified in the PR, for a pattern similar to the following:

    const { createHash } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createHash } = await import('crypto');

    // Insecure example
    const hash = createHash('md4');
  2. Replace weak hash algorithm with SHA3-256.

    const { createHash } = await import('node:crypto');
    // In older Node versions, this is the way to declare
    // const { createHash } = await import('crypto');

    // Secure example
    const hash = createHash('SHA3-256');
  3. Test it

  4. Ship it 🚢 and relax 🌴