Use of Default Cryptographic Key
Description
Use of Default Cryptographic Key occurs when a product uses a default cryptographic key for potentially critical functionality, which simplifies manufacturing or installation but creates security risks if administrators fail to change these defaults. Products commonly employ default keys to streamline deployment processes across organizations. However, when these defaults remain unchanged, attackers can easily bypass authentication mechanisms, decrypt sensitive data, or forge signatures across multiple installations.
Risk
Default cryptographic keys have severe implications. Mass decryption of data across all affected installations. Authentication bypass through forged tokens. Signing key compromise enabling malicious code distribution. TLS/SSL interception using known keys. Session hijacking. Man-in-the-middle attacks. Firmware signing bypass. High likelihood as default keys are often documented or can be extracted from firmware.
Solution
Prohibit default, hard-coded values that do not vary per installation during requirements phase (high effectiveness). Force administrators to change credentials upon installation during architecture and design phase (high effectiveness). Allow product administrators to modify defaults during setup or operation (moderate effectiveness). Generate unique cryptographic keys per device during manufacturing or first boot.
Common Consequences
| Impact | Details |
|---|---|
| Authentication | Scope: Authentication Attackers can gain privileges or assume identity using default cryptographic keys for authentication tokens. |
| Confidentiality | Scope: Confidentiality Encrypted data can be decrypted by anyone with knowledge of the default key. |
Example Code
Vulnerable Code
# Vulnerable: Default cryptographic keys
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
import jwt
import base64
# VULNERABLE: Hard-coded encryption key
DEFAULT_ENCRYPTION_KEY = b'MySuperSecretKey1234567890123456' # 32 bytes for AES-256
DEFAULT_FERNET_KEY = base64.urlsafe_b64encode(DEFAULT_ENCRYPTION_KEY)
# VULNERABLE: Default JWT secret
DEFAULT_JWT_SECRET = "your-256-bit-secret"
# VULNERABLE: Default API signing key
DEFAULT_API_KEY = "sk_live_default_key_12345"
class VulnerableEncryption:
def __init__(self):
# VULNERABLE: Uses default key
self.cipher = Fernet(DEFAULT_FERNET_KEY)
def encrypt(self, data):
# VULNERABLE: All instances use same key
return self.cipher.encrypt(data.encode())
def decrypt(self, token):
# VULNERABLE: Attacker with default key can decrypt
return self.cipher.decrypt(token).decode()
class VulnerableJWT:
def __init__(self):
# VULNERABLE: Default signing secret
self.secret = DEFAULT_JWT_SECRET
def create_token(self, payload):
# VULNERABLE: Anyone knowing default can forge tokens
return jwt.encode(payload, self.secret, algorithm='HS256')
def verify_token(self, token):
# VULNERABLE: Forged tokens accepted
return jwt.decode(token, self.secret, algorithms=['HS256'])
# VULNERABLE: TLS with default certificate/key
TLS_CONFIG = {
"cert_file": "/etc/ssl/default_cert.pem",
"key_file": "/etc/ssl/default_key.pem",
# VULNERABLE: Same cert/key shipped with all devices
}
# VULNERABLE: Firmware signing with default key
FIRMWARE_SIGNING_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu
...
-----END RSA PRIVATE KEY-----
""" # VULNERABLE: Signing key in source code
class VulnerableFirmwareSigner:
def __init__(self):
# VULNERABLE: Default signing key
self.private_key = load_pem_private_key(
FIRMWARE_SIGNING_KEY.encode(),
password=None
)
def sign_firmware(self, firmware_data):
# VULNERABLE: Anyone can sign "legitimate" firmware
signature = self.private_key.sign(
firmware_data,
padding.PKCS1v15(),
hashes.SHA256()
)
return signature
// Vulnerable: Java with default cryptographic keys
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
public class VulnerableCrypto {
// VULNERABLE: Hard-coded AES key
private static final byte[] DEFAULT_AES_KEY =
"DefaultKey123456".getBytes(); // 16 bytes for AES-128
// VULNERABLE: Hard-coded HMAC secret
private static final String DEFAULT_HMAC_SECRET =
"default_hmac_secret_key";
// VULNERABLE: Default RSA key pair (would be loaded from file)
private KeyPair defaultKeyPair;
public VulnerableCrypto() {
// VULNERABLE: Generates same "random" key if seeded consistently
// Or loads from default location
}
public byte[] encrypt(byte[] data) throws Exception {
// VULNERABLE: Uses default key for all encryptions
SecretKeySpec keySpec = new SecretKeySpec(DEFAULT_AES_KEY, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(data);
}
// VULNERABLE: Configuration with default encryption key
public void loadConfig() {
String encryptionKey = System.getProperty("encryption.key");
if (encryptionKey == null) {
// VULNERABLE: Falls back to default
encryptionKey = new String(DEFAULT_AES_KEY);
}
}
}
// VULNERABLE: SSL/TLS with default keystore
public class VulnerableSSL {
// VULNERABLE: Default keystore password
private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit"; // Java default
// VULNERABLE: Default truststore
private static final String DEFAULT_TRUSTSTORE = "/etc/pki/java/cacerts";
public SSLContext createSSLContext() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");
// VULNERABLE: Uses default password
keyStore.load(
new FileInputStream("keystore.jks"),
DEFAULT_KEYSTORE_PASSWORD.toCharArray()
);
// ...
}
}
// Vulnerable: Node.js with default cryptographic keys
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
// VULNERABLE: Hard-coded encryption key
const DEFAULT_ENCRYPTION_KEY = 'this-is-a-default-32-byte-key!!';
const DEFAULT_IV = 'default-iv-1234!';
// VULNERABLE: Default JWT signing key
const DEFAULT_JWT_SECRET = 'your-super-secret-jwt-key';
// VULNERABLE: Default API encryption
class VulnerableApiEncryption {
constructor() {
// VULNERABLE: Uses default key
this.key = Buffer.from(DEFAULT_ENCRYPTION_KEY);
this.iv = Buffer.from(DEFAULT_IV);
}
encrypt(data) {
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
decrypt(data) {
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, this.iv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// VULNERABLE: Session encryption with default
const sessionConfig = {
secret: DEFAULT_JWT_SECRET, // VULNERABLE
cookie: {
secure: true
}
};
// VULNERABLE: Webhook signature with default key
function signWebhook(payload) {
// VULNERABLE: Anyone can forge webhook signatures
const hmac = crypto.createHmac('sha256', DEFAULT_ENCRYPTION_KEY);
hmac.update(JSON.stringify(payload));
return hmac.digest('hex');
}
// VULNERABLE: Default SSH host key (copied from template)
const DEFAULT_SSH_HOST_KEY = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
...
-----END OPENSSH PRIVATE KEY-----
`; // VULNERABLE: Same key on all devices
Fixed Code
# Fixed: Proper cryptographic key management
import os
import secrets
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
import base64
class SecureEncryption:
def __init__(self):
# FIXED: Load key from secure source
self.key = self._load_encryption_key()
def _load_encryption_key(self):
"""FIXED: Load key from environment or generate new one."""
key_b64 = os.environ.get('ENCRYPTION_KEY')
if not key_b64:
raise EnvironmentError(
"ENCRYPTION_KEY not configured. "
"Generate with: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
)
# FIXED: Validate key format
try:
key = base64.urlsafe_b64decode(key_b64)
if len(key) != 32:
raise ValueError("Invalid key length")
except Exception:
raise ValueError("Invalid ENCRYPTION_KEY format")
return key_b64.encode()
def encrypt(self, data):
cipher = Fernet(self.key)
return cipher.encrypt(data.encode())
@staticmethod
def generate_key():
"""FIXED: Generate cryptographically secure key."""
return Fernet.generate_key()
class SecureJWT:
def __init__(self):
# FIXED: Load secret from secure source
self.secret = self._load_jwt_secret()
def _load_jwt_secret(self):
"""FIXED: Require configured JWT secret."""
secret = os.environ.get('JWT_SECRET')
if not secret:
raise EnvironmentError(
"JWT_SECRET not configured. "
"Generate with: python -c \"import secrets; print(secrets.token_hex(32))\""
)
# FIXED: Validate minimum length
if len(secret) < 32:
raise ValueError("JWT_SECRET too short (minimum 32 characters)")
return secret
@staticmethod
def generate_secret():
"""FIXED: Generate secure JWT secret."""
return secrets.token_hex(32)
# FIXED: First-run key generation
class SecureKeyManager:
@staticmethod
def setup():
"""FIXED: Interactive key setup."""
print("=== Cryptographic Key Setup ===\n")
# FIXED: Generate unique keys
encryption_key = Fernet.generate_key().decode()
jwt_secret = secrets.token_hex(32)
api_key = secrets.token_urlsafe(32)
print("Add these to your environment:\n")
print(f"export ENCRYPTION_KEY='{encryption_key}'")
print(f"export JWT_SECRET='{jwt_secret}'")
print(f"export API_SIGNING_KEY='{api_key}'")
print("\nWARNING: Store these securely and never commit to version control!")
# FIXED: Per-device key generation during manufacturing
class SecureProvisioning:
def provision_device(self, serial_number):
"""FIXED: Generate unique keys per device."""
# FIXED: Generate unique device encryption key
device_key = Fernet.generate_key()
# FIXED: Generate unique signing key pair
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# FIXED: Store in secure element or encrypted storage
return {
"serial": serial_number,
"encryption_key": device_key,
"signing_key": private_key
}
// Fixed: Java with proper key management
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class SecureCrypto {
// FIXED: No hard-coded keys
private SecretKey encryptionKey;
public SecureCrypto() throws Exception {
this.encryptionKey = loadEncryptionKey();
}
private SecretKey loadEncryptionKey() throws Exception {
// FIXED: Load from environment or secure storage
String keyB64 = System.getenv("ENCRYPTION_KEY");
if (keyB64 == null || keyB64.isEmpty()) {
throw new SecurityException(
"ENCRYPTION_KEY not configured. Generate using setup command."
);
}
// FIXED: Decode and validate
byte[] keyBytes = Base64.getDecoder().decode(keyB64);
if (keyBytes.length != 32) { // AES-256
throw new SecurityException("Invalid key length");
}
return new SecretKeySpec(keyBytes, "AES");
}
// FIXED: Generate secure key
public static String generateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
SecretKey key = keyGen.generateKey();
return Base64.getEncoder().encodeToString(key.getEncoded());
}
// FIXED: Setup command
public static void main(String[] args) throws Exception {
if (args.length > 0 && "--generate-keys".equals(args[0])) {
System.out.println("=== Key Generation ===\n");
System.out.println("ENCRYPTION_KEY=" + generateKey());
System.out.println("JWT_SECRET=" + generateJWTSecret());
System.out.println("\nStore securely!");
return;
}
// Normal operation
SecureCrypto crypto = new SecureCrypto();
}
private static String generateJWTSecret() {
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
return Base64.getEncoder().encodeToString(bytes);
}
}
// FIXED: Secure keystore handling
public class SecureSSL {
public SSLContext createSSLContext() throws Exception {
// FIXED: Require keystore password from environment
String password = System.getenv("KEYSTORE_PASSWORD");
if (password == null) {
throw new SecurityException("KEYSTORE_PASSWORD not configured");
}
// FIXED: Reject well-known default passwords
if ("changeit".equals(password) || "password".equals(password)) {
throw new SecurityException("Default keystore password detected");
}
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(
new FileInputStream(System.getenv("KEYSTORE_PATH")),
password.toCharArray()
);
// ...
}
}
// Fixed: Node.js with proper key management
const crypto = require('crypto');
// FIXED: No hard-coded keys
class SecureEncryption {
constructor() {
this.key = this._loadEncryptionKey();
}
_loadEncryptionKey() {
const keyHex = process.env.ENCRYPTION_KEY;
if (!keyHex) {
throw new Error(
'ENCRYPTION_KEY not configured. ' +
'Generate with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"'
);
}
// FIXED: Validate key format
if (keyHex.length !== 64) { // 32 bytes = 64 hex chars
throw new Error('Invalid ENCRYPTION_KEY length');
}
return Buffer.from(keyHex, 'hex');
}
encrypt(data) {
// FIXED: Generate random IV for each encryption
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
// FIXED: Include IV with ciphertext
return iv.toString('hex') + ':' + encrypted;
}
decrypt(data) {
const [ivHex, encryptedHex] = data.split(':');
const iv = Buffer.from(ivHex, 'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv);
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// FIXED: Key generation helper
static generateKey() {
return crypto.randomBytes(32).toString('hex');
}
}
// FIXED: Secure JWT configuration
function getJWTSecret() {
const secret = process.env.JWT_SECRET;
if (!secret) {
throw new Error('JWT_SECRET not configured');
}
if (secret.length < 32) {
throw new Error('JWT_SECRET too short');
}
// FIXED: Check for obvious defaults
const defaults = ['secret', 'your-secret', 'jwt-secret', 'changeme'];
if (defaults.some(d => secret.toLowerCase().includes(d))) {
throw new Error('JWT_SECRET appears to be a default value');
}
return secret;
}
// FIXED: Setup script
if (process.argv.includes('--generate-keys')) {
console.log('=== Key Generation ===\n');
console.log(`ENCRYPTION_KEY=${crypto.randomBytes(32).toString('hex')}`);
console.log(`JWT_SECRET=${crypto.randomBytes(32).toString('hex')}`);
console.log(`API_KEY=${crypto.randomBytes(24).toString('base64url')}`);
console.log('\nAdd these to your .env file (never commit to git!)');
process.exit(0);
}
CVE Examples
- CVE-2018-3825: Cloud cluster management with default master encryption key.
- CVE-2016-1561: Backup storage with default SSH public key in authorized_keys.
- CVE-2010-2306: IDS using identical static SSL keys across multiple devices.
Related CWEs
- CWE-1392: Use of Default Credentials (parent)
- CWE-321: Use of Hard-coded Cryptographic Key (related)
- CWE-798: Use of Hard-coded Credentials (related)
References
- MITRE Corporation. "CWE-1394: Use of Default Cryptographic Key." https://cwe.mitre.org/data/definitions/1394.html
- NIST SP 800-57. "Recommendation for Key Management"
- OWASP. "Cryptographic Failures"