Skip to main content

Overview

This vulnerability involves embedding cryptographic keys (used for encryption, decryption, signing, etc.) directly within the application’s source code or configuration files. Similar to hard-coded passwords (CWE-259), these keys often end up in version control systems, exposing them to anyone with repository access. 🔑💻

Business Impact

Hard-coded keys completely undermine the security provided by cryptography. If an attacker obtains the key, they can decrypt sensitive data, forge signatures, or bypass authentication mechanisms that rely on that key. This can lead to data breaches, unauthorized access, and loss of data integrity.

Reference Details

CWE ID: CWE-321 OWASP Top 10 (2021): A02:2021 - Cryptographic Failures Severity: High

Framework-Specific Analysis and Remediation

Like hard-coded passwords, this is a framework-agnostic developer practice issue. The solution is identical: externalize the keys.
  1. Remove the hard-coded key from code/config.
  2. Store the key securely using environment variables, secrets management services (Vault, AWS Secrets Manager, Azure Key Vault), or hardware security modules (HSMs).
  3. Load the key into the application at runtime.
Never commit cryptographic keys to version control.
  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Hard-coding encryption keys (e.g., for Fernet, PyCryptodome) in settings.py or application logic.

Vulnerable Scenario 1: Hard-coded Fernet Key

# utils/encryption.py
from cryptography.fernet import Fernet

# DANGEROUS: Symmetric encryption key hard-coded.
# Modified example to avoid scanners:
_key = b'Z1p5b3V" + "fQ0ZDZjR3d4eXpBQkNERUZHQUJDREVG' # Broken up key
_fernet = Fernet(_key)

def encrypt_data(data):
    return _fernet.encrypt(data.encode())

def decrypt_data(token):
    return _fernet.decrypt(token).decode()

Vulnerable Scenario 2: JWT Secret Key in Settings

# settings.py

# DANGEROUS: Secret key for signing JWTs is hard-coded.
# Modified example to avoid scanners:
JWT_SECRET_KEY = 'my-super-secret' + '-jwt-signing-key' # Broken up key

# Used by a JWT library later...

Mitigation and Best Practices

Load keys from environment variables (os.environ.get()) or a secrets manager. Ensure keys have sufficient entropy and are rotated periodically.

Secure Code Example

# utils/encryption.py (Secure)
import os
from cryptography.fernet import Fernet
from dotenv import load_dotenv
load_dotenv()

# SECURE: Load key from environment variable.
_key_str = os.environ.get('FERNET_ENCRYPTION_KEY')
if not _key_str:
    raise ValueError("FERNET_ENCRYPTION_KEY not set")
_key = _key_str.encode() # Fernet needs bytes
_fernet = Fernet(_key)

def encrypt_data(data):
    return _fernet.encrypt(data.encode())
# ... decrypt ...

# settings.py (Secure)
import os
# SECURE: Load JWT secret from environment variable.
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
if not JWT_SECRET_KEY:
    raise ValueError("JWT_SECRET_KEY not set")
# .env (DO NOT COMMIT)
# Generate a strong key using Fernet.generate_key()
# FERNET_ENCRYPTION_KEY=your_base64_encoded_fernet_key
# JWT_SECRET_KEY=generate_a_strong_random_string_here

Testing Strategy

Use secret scanning tools. Review code and configuration for hard-coded byte strings or string literals that look like keys (high entropy, specific lengths for AES, etc.). Check Git history.