Weakness reference
CWE-324

Use of a Key Past its Expiration Date

This weakness occurs when software continues to use a cryptographic key or password after its intended expiration date. Cryptographic keys are typically…

01Summary

This weakness occurs when software continues to use a cryptographic key or password after its intended expiration date. Cryptographic keys are typically rotated on a schedule to limit the window of exposure if a key is compromised; using an expired key defeats that protection and can allow an attacker with a stolen key to remain undetected for longer than intended.

02How It Happens

Cryptographic systems rely on key rotation as a core security principle: keys are generated with a defined lifetime, and when that lifetime ends, they should be retired and replaced with new ones. This weakness arises when the software either lacks expiration checks entirely, ignores expiration metadata, or continues accepting expired keys due to poor validation logic. Common causes include missing timestamp validation, no enforcement mechanism in the key-loading code, or a fallback that silently accepts expired keys when a newer one is unavailable. The result is that a compromised key remains valid indefinitely, extending the attacker's window of opportunity.

03Real-World Impact

If an attacker obtains a cryptographic key (through theft, insider access, or a prior vulnerability), they can use it to forge signatures, decrypt sensitive data, or impersonate legitimate services—but only while the key is valid. If the software ignores expiration dates, the attacker's access persists far longer than the key's intended lifetime, potentially indefinitely. This is especially critical in scenarios involving API authentication, certificate validation, or session signing, where key rotation is a primary defense against key compromise.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import json
import time
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def verify_signature(message, signature, key_data):
    # key_data is a dict with 'public_key' and 'expires_at'
    # No check of expiration time
    public_key = load_public_key(key_data['public_key'])
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    return True

Why it's vulnerable:
The function accepts and validates a signature using a key without checking whether the key's expires_at timestamp has passed. An expired key is treated as valid.

Fixed pattern
import json
import time
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def verify_signature(message, signature, key_data):
    # Check expiration before using the key
    if time.time() > key_data['expires_at']:
        raise ValueError("Key has expired")
    
    public_key = load_public_key(key_data['public_key'])
    public_key.verify(
        signature,
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    return True
Vulnerable pattern
<?php
function verify_jwt_token($token, $key_data) {
    // $key_data contains 'key' and 'expires_at' fields
    // No expiration check before validation
    $decoded = json_decode(base64_decode($token), true);
    
    if (hash_hmac('sha256', $decoded['payload'], $key_data['key']) === $decoded['signature']) {
        return $decoded['payload'];
    }
    return false;
}
?>

Why it's vulnerable:
The function validates a token using a key without verifying that the key itself has not expired. An attacker with an old, compromised key can still forge valid tokens.

Fixed pattern
<?php
function verify_jwt_token($token, $key_data) {
    // Check key expiration first
    if (time() > $key_data['expires_at']) {
        throw new Exception("Key has expired");
    }
    
    $decoded = json_decode(base64_decode($token), true);
    
    if (hash_hmac('sha256', $decoded['payload'], $key_data['key']) === $decoded['signature']) {
        return $decoded['payload'];
    }
    return false;
}
?>

05Prevention Checklist

Enforce expiration checks at every key-use point.
Before any cryptographic operation (signing, verification, encryption, decryption), validate that the key's expiration timestamp has not passed.
Store and track key metadata.
Maintain expiration dates, creation dates, and rotation schedules alongside keys; never strip this metadata.
Implement automated key rotation.
Use a key management system or scheduled process to retire old keys and provision new ones on a defined schedule.
Reject expired keys explicitly.
Fail closed: if a key is expired, raise an exception or error rather than silently falling back to an older key.
Audit key usage logs.
Monitor which keys are being used and when; alert on any use of keys past their intended expiration.
Test key expiration scenarios.
Include test cases that verify the system correctly rejects expired keys and accepts only current ones.

06Signs You May Already Be Affected

Check your key management logs and cryptographic operation logs for any use of keys with expiration dates in the past. If your system lacks expiration metadata on keys or has no validation logic that checks it, you are vulnerable. Additionally, if you have never rotated a cryptographic key in production, or if your key rotation process is manual and infrequent, the risk window is larger than intended.

07Related Recent Vulnerabilities