CWE-522: Insufficiently Protected Credentials
Overview
This vulnerability occurs when credentials (such as passwords, API keys, or tokens) are stored or transmitted without adequate protection, making them susceptible to theft or misuse.
OWASP Classification
A06:2025 - Insecure Design
Risk
High: Attackers can intercept, steal, or misuse credentials, leading to unauthorized access, data breaches, or privilege escalation.
Remediation Steps
Core principle: Never allow credentials or secrets to be exposed outside a protected trust boundary; credentials must be protected in transit, at rest, and during processing, and must never appear in logs, URLs, or client-visible data.
Locate the insufficiently protected credentials
- Review the flaw details to identify where credentials are stored or transmitted insecurely
- Identify credential type: passwords, API keys, tokens, private keys, database credentials, service account credentials
- Determine the vulnerability: plaintext storage, weak hashing (MD5/SHA-1), hardcoded in code, transmitted over HTTP, in version control
- Trace the data flow: where credentials are created, stored, transmitted, and used
Store credentials securely (Primary Defense)
- Use strong password hashing: Use bcrypt (work factor 12-14), Argon2id (19-47 MiB memory, 2-4 iterations), or scrypt (N=2^17) for password storage
- Never use weak hashing: Avoid MD5, SHA-1, SHA-256 (too fast), unsalted hashes, or custom hashing
- Store secrets in secure vaults: Use secrets management systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, GCP Secret Manager)
- Never hardcode credentials: Don't embed passwords, API keys, or tokens in source code
- Use environment variables as fallback: If vault not available, use environment variables (still better than hardcoding)
Transmit credentials over secure channels
- Always use TLS/HTTPS: Never send credentials over HTTP or unencrypted channels
- Never send credentials in URLs: Don't put passwords or tokens in query parameters (logged in server logs, browser history)
- Use secure cookie attributes: Set
Secureflag (HTTPS only),HttpOnlyflag (no JavaScript access),SameSite=Strict(CSRF protection) - Use authorization headers: Send tokens in
Authorization: Bearer <token>header, not in URL or body
Limit credential exposure and scope
- Use short-lived tokens: Access tokens: 1 hour or less, refresh tokens: 30 days maximum, rotate regularly
- Apply least privilege: Grant minimum necessary permissions to each credential
- Rotate credentials regularly: Change passwords quarterly, rotate API keys annually, immediate rotation after breach
- Use separate credentials per environment: Different credentials for dev, staging, production
- Remove from version control: Use .gitignore for credential files, scan for committed secrets (git-secrets, truffleHog)
Monitor and audit credential usage
- Log all authentication attempts: successful and failed logins, API key usage, token generation
- Alert on suspicious events: failed authentication patterns, unusual access locations, credential exposure attempts
- Track credential age: alert on old passwords, expired certificates, long-lived API keys
- Monitor secrets management access: who accessed which credentials and when
- Implement account lockout: temporary lockout after multiple failed attempts
Test the credential protection fix thoroughly
- Verify passwords are hashed with bcrypt/Argon2 (inspect database)
- Verify no hardcoded credentials in code (grep for "password=", "api_key=")
- Test credentials are transmitted over HTTPS only
- Verify tokens expire after configured time
- Check credentials are not in version control history
- Re-scan with security scanner to confirm the issue is resolved
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code
Common Vulnerable Patterns
Plaintext Password Storage
Why is this vulnerable: Storing passwords in plaintext means that anyone who gains access to the database (through SQL injection, backup exposure, insider threat, or compromised credentials) can immediately read all user passwords. Attackers can then log in as any user, including administrators, without any cracking effort. Since users often reuse passwords across services, attackers can use these credentials to compromise users' accounts on other platforms (credential stuffing). Plaintext passwords also violate compliance requirements (PCI-DSS, GDPR, HIPAA) and create massive liability if disclosed in a breach.
Weak Password Hashing
Why is this vulnerable: Fast hashing algorithms like MD5, SHA-1, or SHA-256 are designed for data integrity, not password storage. Modern GPUs can compute billions of these hashes per second, allowing attackers to crack simple passwords in seconds and complex passwords in hours/days. Unsalted hashes are vulnerable to rainbow table attacks where pre-computed hash databases instantly reveal passwords. Even SHA-256 with salt is too fast - attackers can still try millions of password candidates per second. Password hashing requires deliberately slow algorithms (bcrypt, Argon2id, scrypt) that make cracking computationally expensive.
Hardcoded Credentials in Source Code
Why is this vulnerable: Credentials hardcoded in source code are committed to version control where they persist forever in git history even after deletion. Anyone with repository access (current developers, former employees, compromised accounts) can view these credentials. If the repository is ever made public or leaked, the credentials are exposed globally. Hardcoded credentials can't be rotated without deploying new code, making incident response slow. They're also visible to anyone who gains access to deployed application files or decompiles the application.
Weak JWT Secrets and Long-Lived Tokens
jwt_token = create_token(user, secret="weak"); // Weak secret
expiry = 365_days; // Long-lived tokens
Why is this vulnerable: Weak JWT secrets can be brute-forced by attackers who capture a token, allowing them to forge valid tokens for any user without authentication. Short or predictable secrets (like "secret", "123456") can be cracked in minutes using JWT cracking tools. Long-lived tokens (days, months, or years) extend the window of opportunity for attackers who steal a token - they can use it indefinitely until expiration without needing to re-authenticate. If a token is compromised, there's no way to revoke it before expiration, leaving accounts vulnerable for the entire token lifetime.
Secure Patterns
Strong Password Hashing with bcrypt
Why this works: bcrypt is specifically designed for password hashing with built-in salting and configurable work factor (salt_rounds). Each password gets a unique random salt, preventing rainbow table attacks. The work factor makes hashing deliberately slow - 12 rounds means each password takes ~250ms to hash, which is imperceptible to legitimate users but makes cracking millions of passwords computationally infeasible (years of GPU time per password). As hardware improves, you can increase the work factor (13, 14, 15) without changing code, maintaining security over time. Even if attackers steal the database, they can only crack weak passwords, and strong passwords remain secure indefinitely.
Secrets from Secure Vault Storage
Why this works: Secrets management systems (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) provide centralized, encrypted storage for credentials with access controls, audit logging, and automatic rotation capabilities. Credentials are encrypted at rest using hardware security modules (HSMs), making them useless if storage is compromised. Access to secrets requires authentication and authorization, with all access logged for auditing. Secrets can be rotated centrally without code changes - update the vault, restart applications, and old credentials are invalidated. This prevents credentials from being committed to version control, hardcoded in config files, or exposed in environment variables visible to all processes.
Short-Lived JWT Tokens with Strong Secrets
jwt_secret = vault.get_secret("jwt/secret-key");
access_token = create_token(user_id, jwt_secret, expiry=1_hour);
refresh_token = create_token(user_id, refresh_secret, expiry=30_days);
Why this works: Using a cryptographically strong secret (256+ bits of random data) from a vault prevents JWT forgery - attackers cannot create valid tokens without the secret. Short-lived access tokens (1 hour) limit the damage from token theft - stolen tokens expire quickly, forcing attackers to re-authenticate. Refresh tokens allow seamless re-authentication without repeatedly prompting users for passwords, while their longer expiry (30 days) is still bounded. Storing JWT secrets in a vault (not hardcoded) allows secret rotation if compromised. The two-token pattern balances security (short access token) with user experience (long refresh token for convenience).
HTTPS Enforcement for Credential Transmission
// Express.js middleware
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
return res.redirect(`https://${req.header('host')}${req.url}`);
}
next();
});
// Set secure cookie attributes
res.cookie('session', token, {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: 'strict' // CSRF protection
});
Why this works: HTTPS encrypts all data in transit using TLS, preventing network eavesdropping where attackers on the same WiFi or ISP could intercept credentials in plaintext. The secure cookie flag ensures cookies containing session tokens are only sent over HTTPS, never HTTP. httpOnly prevents JavaScript from reading the cookie, blocking XSS attacks from stealing session tokens. sameSite: 'strict' prevents CSRF attacks by blocking cross-site requests that include the cookie. Redirecting HTTP to HTTPS ensures even if users type http:// or click old links, they're upgraded to secure connections before credentials are transmitted.
Language-Specific Guidance
- Java - BCrypt, Spring Security, environment variables
- JavaScript/Node.js - bcrypt, dotenv, secure password storage
- Python - bcrypt, Argon2, environment variable management