Password Aging with Long Expiration

Description

Password Aging with Long Expiration is a vulnerability that occurs when a product implements password rotation policies with expiration periods that are excessively long, reducing the effectiveness of the aging mechanism. While the system technically requires password changes, the extended timeframe (such as annual or multi-year expiration) provides attackers with ample opportunity to exploit compromised credentials before forced rotation occurs. This weakness represents a middle ground between having no password aging and implementing appropriately short expiration periods, but it may provide a false sense of security by suggesting password management controls exist when their effectiveness is minimal.

Risk

Excessively long password expiration periods increase the window of vulnerability for credential-based attacks. When passwords expire only annually or less frequently, attackers who obtain credentials through theft, cracking, or social engineering have extended periods to exploit access before forced rotation. Password cracking attacks benefit from longer timeframes, as computational resources can be applied over months rather than weeks. The psychological impact on users is also problematic - infrequent password changes may lead to complacency about password security and encourage reuse of the same passwords for extended periods. For compliance-driven environments, long expiration periods may technically satisfy requirements while failing to provide meaningful security benefits.

Solution

Align password expiration periods with risk levels and industry best practices. For standard user accounts, consider 90-day rotation where compliance requires aging, with 30-day periods for privileged accounts. However, recognize that modern security guidance (NIST SP 800-63B) recommends moving away from time-based expiration toward event-based credential changes triggered by suspected compromise. Implement compromised credential detection that forces password changes when breaches affecting user credentials are discovered. Enable multi-factor authentication as a compensating control that reduces reliance on password aging. For high-security environments, combine reasonable expiration periods (60-90 days) with other controls like passwordless authentication, hardware tokens, and continuous authentication. Document risk-based justifications for chosen expiration periods.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

As passwords age, the probability of compromise increases over time. Extended expiration periods allow attackers more time to crack password hashes, exploit stolen credentials, and maintain persistent unauthorized access to systems and data.

Example Code

Vulnerable Configuration (Various)

The following examples demonstrate excessively long password expiration settings:

// Vulnerable: 5-year password expiration
public class VulnerablePasswordPolicy {

    // Excessively long - provides minimal security benefit
    private static final int PASSWORD_EXPIRATION_DAYS = 1825;  // 5 years!

    public boolean isPasswordExpired(User user) {
        if (user.getPasswordLastChanged() == null) {
            return false;  // Never changed = never expires?
        }

        LocalDateTime expiration = user.getPasswordLastChanged()
            .plusDays(PASSWORD_EXPIRATION_DAYS);

        return LocalDateTime.now().isAfter(expiration);
    }
}
# Vulnerable: Active Directory policy with 365-day expiration
# This provides minimal security benefit while causing annual disruption
Set-ADDefaultDomainPasswordPolicy -Identity "contoso.com" `
    -MaxPasswordAge "365.00:00:00" `  # 1 year - too long!
    -MinPasswordAge "1.00:00:00"
# Vulnerable: LDAP/OpenLDAP policy with long expiration
# slapd.conf
overlay ppolicy
ppolicy_default "cn=default,ou=policies,dc=example,dc=com"

# Default policy with excessive expiration
dn: cn=default,ou=policies,dc=example,dc=com
objectClass: pwdPolicy
pwdMaxAge: 31536000  # 365 days in seconds - too long!
pwdExpireWarning: 604800  # 7 days warning
# Vulnerable: PAM configuration with annual expiration
# /etc/login.defs
PASS_MAX_DAYS   365    # Too long for meaningful security
PASS_MIN_DAYS   0
PASS_WARN_AGE   7

Fixed Configuration (Various)

// Fixed: Risk-based password expiration
public class SecurePasswordPolicy {

    // Standard users: 90 days (configurable)
    private static final int STANDARD_USER_EXPIRATION_DAYS = 90;

    // Privileged accounts: 30 days
    private static final int PRIVILEGED_USER_EXPIRATION_DAYS = 30;

    // Service accounts: 90 days with monitoring
    private static final int SERVICE_ACCOUNT_EXPIRATION_DAYS = 90;

    public int getExpirationDays(User user) {
        if (user.hasRole(Role.ADMIN) || user.hasRole(Role.PRIVILEGED)) {
            return PRIVILEGED_USER_EXPIRATION_DAYS;
        }
        return STANDARD_USER_EXPIRATION_DAYS;
    }

    public boolean isPasswordExpired(User user) {
        if (user.getPasswordLastChanged() == null) {
            return true;  // Force initial password change
        }

        // Check for breach-triggered expiration first
        if (breachDetectionService.isCredentialCompromised(user)) {
            return true;  // Immediate expiration on breach
        }

        int expirationDays = getExpirationDays(user);
        LocalDateTime expiration = user.getPasswordLastChanged()
            .plusDays(expirationDays);

        return LocalDateTime.now().isAfter(expiration);
    }

    // Modern approach: Event-based expiration
    public void handleBreachDetection(String breachedCredentialHash) {
        List<User> affectedUsers = userRepository
            .findByPasswordHashIn(breachedCredentialHash);

        for (User user : affectedUsers) {
            user.setForcePasswordChange(true);
            user.setPasswordExpiredReason("Credential found in data breach");
            notificationService.sendBreachNotification(user);
        }
    }
}
# Fixed: Active Directory with appropriate expiration
# Privileged accounts: 30 days
# Standard accounts: 90 days (or consider no expiration with MFA)

# Set default domain policy for standard users
Set-ADDefaultDomainPasswordPolicy -Identity "contoso.com" `
    -MaxPasswordAge "90.00:00:00" `  # 90 days
    -MinPasswordAge "1.00:00:00" `
    -PasswordHistoryCount 24 `
    -ComplexityEnabled $true

# Create fine-grained policy for admins
New-ADFineGrainedPasswordPolicy -Name "AdminPasswordPolicy" `
    -Precedence 10 `
    -MaxPasswordAge "30.00:00:00" `  # 30 days for admins
    -MinPasswordAge "1.00:00:00" `
    -PasswordHistoryCount 24 `
    -ComplexityEnabled $true

# Apply to admin group
Add-ADFineGrainedPasswordPolicySubject `
    -Identity "AdminPasswordPolicy" `
    -Subjects "Domain Admins"
# Fixed: Modern approach - breach detection over time-based expiration
# password-policy.yml
password_policy:
  # Time-based expiration (for compliance)
  standard_users:
    max_age_days: 90
    warning_days: 14

  privileged_users:
    max_age_days: 30
    warning_days: 7

  # Event-based expiration (modern approach)
  breach_detection:
    enabled: true
    sources:
      - haveibeenpwned
      - internal_siem
    action: force_immediate_change

  # Compensating controls
  mfa:
    required: true
    allows_extended_expiration: true  # With MFA, can extend to 180 days

  # For compliance reporting
  compliance:
    pci_dss:
      enabled: true
      max_age_days: 90

The fix implements risk-based expiration periods with shorter timeframes for privileged accounts and adds breach detection for event-based password rotation.


Exploited in the Wild

Extended Persistence Through Stale Credentials (Multiple APT Campaigns)

Advanced threat actors have maintained network access for years using credentials that were stolen early in intrusions and never rotated due to excessively long expiration policies. Annual password rotation meant attackers had 11 months of guaranteed access after compromise before any forced rotation might disrupt their operations.

Compliance-Driven False Security (Healthcare Sector, Multiple Incidents)

Healthcare organizations meeting minimal HIPAA password requirements with annual rotation suffered breaches where attackers exploited the long window between password changes. Investigations revealed that technical compliance provided false assurance while offering minimal actual protection.

Service Account Exploitation (Financial Sector, 2019)

Financial institution service accounts with annual password rotation were compromised and used for lateral movement over many months. The long expiration window allowed attackers to establish persistent access, exfiltrate data, and prepare for larger attacks without triggering credential-based detection.


Tools to Test/Exploit

  • CrackMapExec — Identifies accounts with long-unchanged passwords and tests credential validity over time.

  • PingCastle — Active Directory security assessment tool that identifies password policy weaknesses including excessive expiration periods.

  • Specops Password Auditor — Free tool for analyzing Active Directory password policies and identifying weak configurations.


CVE Examples

Password aging configuration weaknesses are typically not assigned individual CVEs as they represent policy decisions rather than software vulnerabilities. However, long expiration periods have been cited as contributing factors in breach investigations and compliance findings.


References

  1. MITRE Corporation. "CWE-263: Password Aging with Long Expiration." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/263.html

  2. NIST. "Digital Identity Guidelines: Authentication and Lifecycle Management." SP 800-63B. https://pages.nist.gov/800-63-3/sp800-63b.html

  3. NCSC UK. "Password Policy: Updating Your Approach." https://www.ncsc.gov.uk/collection/passwords

  4. PCI Security Standards Council. "PCI DSS Password Requirements." https://www.pcisecuritystandards.org/