Insecure Randomness

ID

swift.insecure_randomness

Severity

high

Resource

Predictability

Language

Swift

Tags

CWE:330, CWE:332, CWE:336, CWE:337, CWE:338, MASWE:0027, NIST.SP.800-53, OWASP:2021:A2, PCI-DSS:6.5.3

Description

Cryptographically weak pseudo-random number generators (PRNGs) should not be used in security-sensitive contexts. Standard PRNGs like arc4random(), drand48(), or Int.random() are designed for general-purpose use (simulations, games, testing) but are not suitable for cryptographic purposes.

These functions produce predictable sequences that can be guessed or reproduced by attackers, especially when: - The seed is predictable or derived from weak entropy - The algorithm has known mathematical weaknesses - Side-channel attacks can observe the internal state

In Swift, susceptible code might look like this:

import Foundation

func generateSessionToken() -> String {
    // FLAW: Using insecure random for security-sensitive token
    let randomValue = Int.random(in: 0...Int.max)
    return String(format: "%016x", randomValue)
}

func generateEncryptionKey() -> Data {
    var bytes = [UInt8](repeating: 0, count: 32)
    for i in 0..<bytes.count {
        // FLAW: Using arc4random for cryptographic key
        bytes[i] = UInt8(arc4random_uniform(256))
    }
    return Data(bytes)
}

func generatePassword() -> String {
    let length = 16
    let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    // FLAW: Using drand48 for password generation
    return String((0..<length).map { _ in
        chars[chars.index(chars.startIndex, offsetBy: Int(drand48() * Double(chars.count)))]
    })
}

In these instances, insecure PRNGs are used to generate security-critical values (session tokens, encryption keys, passwords). An attacker who can predict or reproduce these values could: - Hijack user sessions - Decrypt encrypted data - Bypass authentication mechanisms - Forge cryptographic signatures

Rationale

The use of cryptographically weak PRNGs in security contexts poses several risks:

Predictability: Standard PRNGs use deterministic algorithms. If an attacker can observe several outputs or guess the seed, they may be able to predict future values.

Insufficient Entropy: Functions like arc4random() may not gather sufficient entropy from secure sources on all platforms.

Mathematical Weaknesses: Algorithms like RC4 (used by arc4random) and linear congruential generators (used by drand48) have known cryptographic weaknesses.

Reproducibility: Given the same seed, these PRNGs produce identical sequences, enabling replay attacks.

Security-sensitive contexts include: - Session tokens and identifiers - CSRF tokens - Password generation - Cryptographic keys and initialization vectors - Nonces and salts - Authentication challenges - Random UUIDs for security purposes

Remediation

For security-sensitive random number generation in Swift, use cryptographically secure alternatives:

Option 1: Use SecRandomCopyBytes (Security framework)

import Foundation
import Security

func generateSessionToken() -> String {
    var bytes = [UInt8](repeating: 0, count: 32)
    // FIXED: Using SecRandomCopyBytes for cryptographically secure random
    let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)

    guard status == errSecSuccess else {
        fatalError("Failed to generate secure random bytes")
    }

    return bytes.map { String(format: "%02x", $0) }.joined()
}

func generateEncryptionKey() -> Data {
    var bytes = [UInt8](repeating: 0, count: 32)
    // FIXED: Using SecRandomCopyBytes for key generation
    let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)

    guard status == errSecSuccess else {
        fatalError("Failed to generate secure random key")
    }

    return Data(bytes)
}

Option 2: Use SystemRandomNumberGenerator (Swift standard library)

import Foundation

func generateSessionToken() -> String {
    // FIXED: Using SystemRandomNumberGenerator (cryptographically secure)
    var generator = SystemRandomNumberGenerator()
    let randomBytes = (0..<32).map { _ in UInt8.random(in: 0...255, using: &generator) }
    return randomBytes.map { String(format: "%02x", $0) }.joined()
}

func generatePassword(length: Int = 16) -> String {
    let chars = Array("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()")
    // FIXED: Using SystemRandomNumberGenerator for secure selection
    var generator = SystemRandomNumberGenerator()
    return String((0..<length).map { _ in
        chars[Int.random(in: 0..<chars.count, using: &generator)]
    })
}

Option 3: Use CryptoKit (iOS 13+, macOS 10.15+)

import CryptoKit

func generateEncryptionKey() -> SymmetricKey {
    // FIXED: CryptoKit automatically uses secure random
    return SymmetricKey(size: .bits256)
}

func generateNonce() -> Data {
    // FIXED: SystemRandomNumberGenerator can be used
    // _on most platforms_ for cryptographic uses
    var generator = SystemRandomNumberGenerator()
    var nonce = Data(count: 12)
    nonce.withUnsafeMutableBytes { bytes in
        for i in 0..<bytes.count {
            bytes[i] = UInt8.random(in: 0...255, using: &generator)
        }
    }
    return nonce
}

print( generateNonce().base64EncodedString(options: .endLineWithLineFeed) )

Important Notes:

  1. Always use secure random for security-sensitive operations: Session tokens, keys, passwords, nonces, salts, IVs, etc.

  2. Standard PRNGs are fine for non-security contexts: Games, simulations, testing, UI animations, etc.

  3. Platform Availability:

    • SecRandomCopyBytes is available on all Apple platforms

    • SystemRandomNumberGenerator requires Swift 4.2+ (iOS 8+, macOS 10.10+)

    • CryptoKit requires iOS 13+, macOS 10.15+

  4. Performance: Secure random is slightly slower than insecure PRNG, but the overhead is negligible for typical security operations.

Configuration

The detector can be configured to check only security-sensitive contexts or all uses of insecure random:

  properties:
    # Only flag insecure random in security contexts (default: true)
    checkSecurityContext: true

    # Pattern to identify security-sensitive contexts (regex)
    securityContextPattern: (auth|csrf|digest|guid|hash|key|nonce|passwd|password|salt|secret|session|token|user|uuid|encrypt|random)

To flag all uses of insecure random regardless of context:

swift.insecure_randomness:
  properties:
    checkSecurityContext: false

References