Use of Hard-coded, Security-relevant Constants

Description

Use of Hard-coded, Security-relevant Constants is a vulnerability where a product uses hard-coded constants instead of configurable values for security-critical parameters. This includes hard-coded cryptographic keys, salts, initialization vectors, algorithm identifiers, iteration counts, key sizes, timeout values, and other security-relevant parameters. When these values are embedded directly in code, they become difficult to change, may be exposed through reverse engineering, and create consistency issues across deployments where different security configurations might be needed.

Risk

Hard-coded security constants create significant vulnerabilities. Cryptographic keys embedded in code can be extracted through reverse engineering, decompilation, or source code leaks, compromising all systems using that key. Hard-coded salts eliminate the uniqueness benefit that salts provide, making rainbow table attacks feasible. Fixed iteration counts may become insufficient as computing power increases but cannot be updated without code changes. Hard-coded timeouts may be inappropriately long or short for different deployment environments. When security constants are discovered, changing them requires code updates, testing, and deployment rather than simple configuration changes, leading to delayed responses to security incidents.

Solution

Externalize all security-relevant constants to configuration files, environment variables, or secure parameter stores. Use key management systems for cryptographic keys. Generate unique salts per operation rather than using fixed values. Make iteration counts and key sizes configurable with secure defaults. Store security timeouts in configuration that can be adjusted per environment. When constants must be in code, use named constants with clear documentation rather than magic numbers. Implement configuration validation to ensure security parameters meet minimum requirements. Use different security configurations for development, testing, and production environments.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Hard-coded cryptographic keys or other secrets can be extracted from code, enabling attackers to decrypt sensitive data or bypass authentication.
Access ControlScope: Access Control

Bypass Protection Mechanism - Hard-coded security parameters like timeouts or retry limits may be insufficient, allowing brute force attacks or session fixation.
OtherScope: Other

Reduce Maintainability - Hard-coded constants make it difficult to update security parameters in response to new threats or changing requirements without code changes.

Example Code

Vulnerable Code

// Vulnerable: Hard-coded security-relevant constants
public class VulnerableCrypto {

    // Vulnerable: Hard-coded encryption key
    private static final byte[] SECRET_KEY =
        "MySecretKey12345".getBytes(StandardCharsets.UTF_8);

    // Vulnerable: Hard-coded IV (should be random per encryption)
    private static final byte[] INIT_VECTOR =
        {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};

    // Vulnerable: Hard-coded salt (defeats purpose of salting)
    private static final String PASSWORD_SALT = "FixedSalt123";

    // Vulnerable: Hard-coded iteration count (may become insecure)
    private static final int PBKDF2_ITERATIONS = 1000;  // Too low!

    // Vulnerable: Hard-coded key size
    private static final int KEY_SIZE = 128;  // Should be 256

    public byte[] encrypt(String plaintext) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(INIT_VECTOR);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        return cipher.doFinal(plaintext.getBytes());
    }

    public String hashPassword(String password) {
        // Vulnerable: Same salt for all passwords
        String salted = PASSWORD_SALT + password;
        return DigestUtils.sha256Hex(salted);
    }
}

// Vulnerable: Hard-coded security timeouts
public class VulnerableSession {
    // Vulnerable: Hard-coded timeout values
    private static final int SESSION_TIMEOUT = 3600;      // 1 hour
    private static final int TOKEN_EXPIRY = 86400;        // 24 hours
    private static final int MAX_LOGIN_ATTEMPTS = 3;
    private static final int LOCKOUT_DURATION = 300;      // 5 minutes

    // These may be too long or too short for different environments
}
# Vulnerable: Hard-coded security constants in Python
import hashlib
from Crypto.Cipher import AES

class VulnerableSecurity:
    # Vulnerable: Hard-coded API key
    API_SECRET_KEY = "sk_live_hardcoded_key_12345"

    # Vulnerable: Hard-coded encryption key
    ENCRYPTION_KEY = b"0123456789ABCDEF"  # 16 bytes for AES-128

    # Vulnerable: Hard-coded JWT secret
    JWT_SECRET = "super_secret_jwt_key"

    # Vulnerable: Hard-coded HMAC key
    HMAC_KEY = b"hmac_secret_key_value"

    # Vulnerable: Hard-coded password pepper
    PASSWORD_PEPPER = "application_pepper_2023"

    def encrypt_data(self, data):
        # Vulnerable: Using hard-coded key and no IV
        cipher = AES.new(self.ENCRYPTION_KEY, AES.MODE_ECB)  # ECB is also bad
        return cipher.encrypt(self._pad(data))

    def hash_password(self, password):
        # Vulnerable: Hard-coded pepper, no unique salt
        peppered = password + self.PASSWORD_PEPPER
        return hashlib.sha256(peppered.encode()).hexdigest()

    def generate_token(self, user_id):
        # Vulnerable: Hard-coded JWT secret
        import jwt
        return jwt.encode(
            {"user_id": user_id},
            self.JWT_SECRET,
            algorithm="HS256"
        )


# Vulnerable: Hard-coded security limits
MAX_FILE_SIZE = 10485760       # 10MB - might need adjustment
MAX_REQUEST_SIZE = 1048576     # 1MB - fixed limit
RATE_LIMIT = 100               # requests per minute
BCRYPT_ROUNDS = 10             # Should be configurable and higher
// Vulnerable: Hard-coded constants in Node.js
const crypto = require('crypto');

// Vulnerable: Hard-coded encryption key
const ENCRYPTION_KEY = 'abcdefghijklmnop';  // 16 bytes

// Vulnerable: Hard-coded HMAC secret
const HMAC_SECRET = 'hmac_secret_123';

// Vulnerable: Hard-coded JWT secret
const JWT_SECRET = 'my_jwt_secret_key';

// Vulnerable: Hard-coded session settings
const SESSION_CONFIG = {
    secret: 'session_secret_value',      // Hard-coded
    maxAge: 3600000,                      // Fixed 1 hour
    secure: false,                        // Should depend on environment
    sameSite: 'lax'                       // Fixed policy
};

// Vulnerable: Hard-coded crypto parameters
const CRYPTO_CONFIG = {
    algorithm: 'aes-128-cbc',            // Should be configurable
    iterations: 10000,                    // Fixed PBKDF2 iterations
    keyLength: 16,                        // Fixed key length
    saltLength: 16                        // Fixed salt length
};

function encryptData(data) {
    // Vulnerable: Using hard-coded key
    const cipher = crypto.createCipheriv(
        'aes-128-cbc',
        ENCRYPTION_KEY,
        Buffer.alloc(16)  // Zero IV - even worse!
    );
    return cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
}

function signData(data) {
    // Vulnerable: Hard-coded HMAC key
    return crypto.createHmac('sha256', HMAC_SECRET)
        .update(data)
        .digest('hex');
}
<?php
// Vulnerable: Hard-coded security constants in PHP

class VulnerableCrypto {
    // Vulnerable: Hard-coded encryption key
    private const ENCRYPTION_KEY = 'MySuperSecretKey';

    // Vulnerable: Hard-coded IV
    private const IV = '1234567890123456';

    // Vulnerable: Hard-coded HMAC key
    private const HMAC_KEY = 'hmac_key_fixed';

    // Vulnerable: Hard-coded bcrypt cost (too low)
    private const BCRYPT_COST = 4;  // Should be at least 12

    // Vulnerable: Hard-coded password requirements
    private const MIN_PASSWORD_LENGTH = 6;   // Too short
    private const REQUIRE_SPECIAL_CHAR = false;  // Should be true

    public function encrypt($data) {
        // Vulnerable: Using hard-coded key and IV
        return openssl_encrypt(
            $data,
            'AES-128-CBC',
            self::ENCRYPTION_KEY,
            0,
            self::IV
        );
    }

    public function hashPassword($password) {
        // Vulnerable: Fixed low cost factor
        return password_hash($password, PASSWORD_BCRYPT, [
            'cost' => self::BCRYPT_COST
        ]);
    }

    public function signData($data) {
        // Vulnerable: Hard-coded HMAC key
        return hash_hmac('sha256', $data, self::HMAC_KEY);
    }
}

// Vulnerable: Hard-coded security timeouts
define('SESSION_TIMEOUT', 3600);
define('CSRF_TOKEN_EXPIRY', 1800);
define('PASSWORD_RESET_EXPIRY', 86400);
define('MAX_LOGIN_ATTEMPTS', 5);
?>

Fixed Code

// Fixed: Configurable security constants
public class SecureCrypto {
    private final SecureRandom secureRandom;
    private final SecurityConfig config;
    private final KeyManagementService keyService;

    public SecureCrypto(SecurityConfig config, KeyManagementService keyService) {
        this.config = config;
        this.keyService = keyService;
        this.secureRandom = new SecureRandom();
    }

    public EncryptedData encrypt(String plaintext) throws Exception {
        // Fixed: Get key from key management service
        byte[] key = keyService.getEncryptionKey();

        // Fixed: Generate random IV for each encryption
        byte[] iv = new byte[config.getIvSize()];
        secureRandom.nextBytes(iv);

        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        // Fixed: Algorithm configurable
        Cipher cipher = Cipher.getInstance(config.getCipherAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

        byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));

        // Return IV with ciphertext (IV doesn't need to be secret)
        return new EncryptedData(iv, ciphertext);
    }

    public String hashPassword(String password) {
        // Fixed: Generate unique salt per password
        byte[] salt = new byte[config.getSaltSize()];
        secureRandom.nextBytes(salt);

        // Fixed: Configurable iteration count from config
        int iterations = config.getPbkdf2Iterations();
        int keyLength = config.getDerivedKeyLength();

        PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            salt,
            iterations,
            keyLength
        );

        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] hash = factory.generateSecret(spec).getEncoded();

        // Return salt and hash together
        return Base64.getEncoder().encodeToString(salt) + ":" +
               Base64.getEncoder().encodeToString(hash);
    }
}

// Fixed: Security configuration class
public class SecurityConfig {
    private final int ivSize;
    private final int saltSize;
    private final int pbkdf2Iterations;
    private final int derivedKeyLength;
    private final String cipherAlgorithm;

    public SecurityConfig() {
        // Load from environment or config file
        this.ivSize = getIntProperty("CRYPTO_IV_SIZE", 16);
        this.saltSize = getIntProperty("CRYPTO_SALT_SIZE", 32);
        this.pbkdf2Iterations = getIntProperty("PBKDF2_ITERATIONS", 310000);
        this.derivedKeyLength = getIntProperty("DERIVED_KEY_LENGTH", 256);
        this.cipherAlgorithm = getProperty("CIPHER_ALGORITHM", "AES/GCM/NoPadding");

        // Validate minimum security requirements
        validateConfig();
    }

    private void validateConfig() {
        if (pbkdf2Iterations < 100000) {
            throw new SecurityException("PBKDF2 iterations too low");
        }
        if (derivedKeyLength < 256) {
            throw new SecurityException("Key length too short");
        }
    }

    // Getters...
}
# Fixed: Configurable security parameters in Python
import os
import secrets
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

class SecureSecurity:
    def __init__(self, config=None):
        self.config = config or SecurityConfig()
        self.key_service = KeyManagementService()

    def encrypt_data(self, data: bytes) -> dict:
        # Fixed: Get key from key management service
        key = self.key_service.get_encryption_key()

        # Fixed: Generate random IV for each encryption
        iv = secrets.token_bytes(self.config.iv_size)

        # Fixed: Use authenticated encryption (GCM)
        cipher = Cipher(
            algorithms.AES(key),
            modes.GCM(iv),
            backend=default_backend()
        )
        encryptor = cipher.encryptor()
        ciphertext = encryptor.update(data) + encryptor.finalize()

        return {
            'iv': iv,
            'ciphertext': ciphertext,
            'tag': encryptor.tag
        }

    def hash_password(self, password: str) -> str:
        # Fixed: Generate unique salt per password
        salt = secrets.token_bytes(self.config.salt_size)

        # Fixed: Configurable iterations
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=self.config.derived_key_length,
            salt=salt,
            iterations=self.config.pbkdf2_iterations,
            backend=default_backend()
        )

        key = kdf.derive(password.encode())

        import base64
        return base64.b64encode(salt).decode() + ':' + base64.b64encode(key).decode()

    def generate_token(self, user_id: str) -> str:
        import jwt
        # Fixed: Get secret from key service
        secret = self.key_service.get_jwt_secret()
        return jwt.encode(
            {"user_id": user_id},
            secret,
            algorithm=self.config.jwt_algorithm
        )


class SecurityConfig:
    """Security configuration loaded from environment."""

    def __init__(self):
        # Fixed: All parameters from environment with secure defaults
        self.iv_size = int(os.environ.get('CRYPTO_IV_SIZE', '12'))  # 96 bits for GCM
        self.salt_size = int(os.environ.get('CRYPTO_SALT_SIZE', '32'))
        self.pbkdf2_iterations = int(os.environ.get('PBKDF2_ITERATIONS', '310000'))
        self.derived_key_length = int(os.environ.get('DERIVED_KEY_LENGTH', '32'))
        self.jwt_algorithm = os.environ.get('JWT_ALGORITHM', 'HS256')

        # Fixed: Validate minimum requirements
        self._validate()

    def _validate(self):
        if self.pbkdf2_iterations < 100000:
            raise ValueError("PBKDF2 iterations must be at least 100000")
        if self.salt_size < 16:
            raise ValueError("Salt size must be at least 16 bytes")
// Fixed: Configurable security constants in Node.js
const crypto = require('crypto');

class SecurityConfig {
    constructor() {
        // Fixed: Load from environment
        this.ivSize = parseInt(process.env.CRYPTO_IV_SIZE || '12');
        this.saltSize = parseInt(process.env.CRYPTO_SALT_SIZE || '32');
        this.pbkdf2Iterations = parseInt(process.env.PBKDF2_ITERATIONS || '310000');
        this.keyLength = parseInt(process.env.KEY_LENGTH || '32');
        this.algorithm = process.env.CIPHER_ALGORITHM || 'aes-256-gcm';

        // Fixed: Validate configuration
        this.validate();
    }

    validate() {
        if (this.pbkdf2Iterations < 100000) {
            throw new Error('PBKDF2 iterations too low');
        }
        if (this.keyLength < 32) {
            throw new Error('Key length too short');
        }
    }
}

class SecureCrypto {
    constructor(config, keyService) {
        this.config = config || new SecurityConfig();
        this.keyService = keyService;
    }

    async encrypt(data) {
        // Fixed: Get key from key service
        const key = await this.keyService.getEncryptionKey();

        // Fixed: Generate random IV for each encryption
        const iv = crypto.randomBytes(this.config.ivSize);

        // Fixed: Use authenticated encryption
        const cipher = crypto.createCipheriv(this.config.algorithm, key, iv);

        let ciphertext = cipher.update(data, 'utf8');
        ciphertext = Buffer.concat([ciphertext, cipher.final()]);

        const authTag = cipher.getAuthTag();

        return {
            iv: iv.toString('base64'),
            ciphertext: ciphertext.toString('base64'),
            authTag: authTag.toString('base64')
        };
    }

    hashPassword(password) {
        // Fixed: Generate unique salt
        const salt = crypto.randomBytes(this.config.saltSize);

        // Fixed: Use configurable iterations
        const hash = crypto.pbkdf2Sync(
            password,
            salt,
            this.config.pbkdf2Iterations,
            this.config.keyLength,
            'sha256'
        );

        return salt.toString('base64') + ':' + hash.toString('base64');
    }
}

// Fixed: Session config from environment
const sessionConfig = {
    secret: process.env.SESSION_SECRET,  // Required, no default
    maxAge: parseInt(process.env.SESSION_MAX_AGE || '3600000'),
    secure: process.env.NODE_ENV === 'production',
    sameSite: process.env.SESSION_SAME_SITE || 'strict'
};

if (!sessionConfig.secret) {
    throw new Error('SESSION_SECRET environment variable required');
}

module.exports = { SecurityConfig, SecureCrypto, sessionConfig };
<?php
// Fixed: Configurable security parameters in PHP

class SecurityConfig {
    private int $ivSize;
    private int $saltSize;
    private int $pbkdf2Iterations;
    private int $bcryptCost;
    private string $cipherAlgorithm;

    public function __construct() {
        // Fixed: Load from environment with secure defaults
        $this->ivSize = (int)getenv('CRYPTO_IV_SIZE') ?: 16;
        $this->saltSize = (int)getenv('CRYPTO_SALT_SIZE') ?: 32;
        $this->pbkdf2Iterations = (int)getenv('PBKDF2_ITERATIONS') ?: 310000;
        $this->bcryptCost = (int)getenv('BCRYPT_COST') ?: 12;
        $this->cipherAlgorithm = getenv('CIPHER_ALGORITHM') ?: 'aes-256-gcm';

        $this->validate();
    }

    private function validate(): void {
        if ($this->pbkdf2Iterations < 100000) {
            throw new SecurityException('PBKDF2 iterations too low');
        }
        if ($this->bcryptCost < 10) {
            throw new SecurityException('Bcrypt cost too low');
        }
    }

    // Getters...
    public function getIvSize(): int { return $this->ivSize; }
    public function getSaltSize(): int { return $this->saltSize; }
    public function getPbkdf2Iterations(): int { return $this->pbkdf2Iterations; }
    public function getBcryptCost(): int { return $this->bcryptCost; }
    public function getCipherAlgorithm(): string { return $this->cipherAlgorithm; }
}

class SecureCrypto {
    private SecurityConfig $config;
    private KeyManagementService $keyService;

    public function __construct(SecurityConfig $config, KeyManagementService $keyService) {
        $this->config = $config;
        $this->keyService = $keyService;
    }

    public function encrypt(string $data): array {
        // Fixed: Get key from key service
        $key = $this->keyService->getEncryptionKey();

        // Fixed: Generate random IV
        $iv = random_bytes($this->config->getIvSize());

        // Fixed: Use authenticated encryption
        $ciphertext = openssl_encrypt(
            $data,
            $this->config->getCipherAlgorithm(),
            $key,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        return [
            'iv' => base64_encode($iv),
            'ciphertext' => base64_encode($ciphertext),
            'tag' => base64_encode($tag)
        ];
    }

    public function hashPassword(string $password): string {
        // Fixed: Use configurable bcrypt cost
        return password_hash($password, PASSWORD_BCRYPT, [
            'cost' => $this->config->getBcryptCost()
        ]);
    }
}

// Fixed: Security timeouts from environment
class SecurityTimeouts {
    public static function getSessionTimeout(): int {
        return (int)getenv('SESSION_TIMEOUT') ?: 3600;
    }

    public static function getCsrfTokenExpiry(): int {
        return (int)getenv('CSRF_TOKEN_EXPIRY') ?: 1800;
    }

    public static function getPasswordResetExpiry(): int {
        return (int)getenv('PASSWORD_RESET_EXPIRY') ?: 3600;  // 1 hour, not 24
    }

    public static function getMaxLoginAttempts(): int {
        return (int)getenv('MAX_LOGIN_ATTEMPTS') ?: 5;
    }
}
?>

CVE Examples

  • CVE-2022-26138: Atlassian Confluence Questions used hard-coded password for the disabledsystemuser account.
  • CVE-2021-29078: NetModule Router Software contained hard-coded cryptographic keys.
  • CVE-2020-4574: IBM Security Key Lifecycle Manager used hard-coded credentials.

References

  1. MITRE Corporation. "CWE-547: Use of Hard-coded, Security-relevant Constants." https://cwe.mitre.org/data/definitions/547.html
  2. OWASP. "Cryptographic Storage Cheat Sheet."
  3. NIST. "Key Management Guidelines."