# Timing Vulnerability Patterns

> This document describes common timing vulnerability patterns and how to detect them.

- Skill: `tools-only/timing-vulnerability-patterns` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/timing-vulnerability-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/timing-vulnerability-patterns/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-29
- Page: https://skillmd.com/skills/tools-only/timing-vulnerability-patterns

---

# Timing Vulnerability Patterns

This document describes common timing vulnerability patterns and how to detect them.

## Pattern 1: Missing Expiration Checks

### Description
Code sets an expiration time but never validates it before use.

### Example (Python)
```python
# VULNERABLE: Token has expiration but it's never checked
def validate_token(token):
    decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
    user_id = decoded['user_id']
    # Missing: Check decoded['exp'] against current time
    return user_id

# SECURE: Properly check expiration
def validate_token(token):
    try:
        decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        # jwt.decode automatically checks 'exp' claim
        return decoded['user_id']
    except jwt.ExpiredSignatureError:
        raise AuthenticationError("Token expired")
```

### Detection
- Search for token/session creation with expiration field
- Verify corresponding validation code checks expiration
- Look for: `exp`, `expires_at`, `valid_until`, `expiry`

### Example (JavaScript)
```javascript
// VULNERABLE: Session has timeout but never enforced
class Session {
  constructor(userId) {
    this.userId = userId;
    this.createdAt = Date.now();
    this.expiresAt = Date.now() + (30 * 60 * 1000); // 30 min
  }

  isValid() {
    // Missing: Check if Date.now() > this.expiresAt
    return this.userId != null;
  }
}

// SECURE: Check expiration
class Session {
  constructor(userId) {
    this.userId = userId;
    this.createdAt = Date.now();
    this.expiresAt = Date.now() + (30 * 60 * 1000);
  }

  isValid() {
    return this.userId != null && Date.now() < this.expiresAt;
  }
}
```

## Pattern 2: Excessive Expiration Times

### Description
Security-critical tokens/sessions with unreasonably long expiration times.

### Example (Python)
```python
# VULNERABLE: Password reset token valid for 7 days
def create_reset_token(user):
    token = secrets.token_urlsafe(32)
    expiry = datetime.now() + timedelta(days=7)  # Too long!
    save_reset_token(user, token, expiry)
    return token

# SECURE: Reasonable 1-hour expiration
def create_reset_token(user):
    token = secrets.token_urlsafe(32)
    expiry = datetime.now() + timedelta(hours=1)  # Appropriate
    save_reset_token(user, token, expiry)
    return token
```

### Detection
- Look for `timedelta`, `setTimeout`, `Duration` with large values
- Check: days > 1 for reset tokens, hours > 24 for sessions
- Keywords: `days=`, `hours=`, `minutes=`, `expires_in`

### Example (Java)
```java
// VULNERABLE: JWT access token valid for 30 days
public String generateAccessToken(User user) {
    return Jwts.builder()
        .setSubject(user.getId())
        .setExpiration(new Date(System.currentTimeMillis() +
            30L * 24 * 60 * 60 * 1000))  // 30 days - too long!
        .signWith(key)
        .compact();
}

// SECURE: 15-minute expiration
public String generateAccessToken(User user) {
    return Jwts.builder()
        .setSubject(user.getId())
        .setExpiration(new Date(System.currentTimeMillis() +
            15L * 60 * 1000))  // 15 minutes
        .signWith(key)
        .compact();
}
```

## Pattern 3: No Rate Limiting

### Description
Security-sensitive operations lack rate limiting or throttling.

### Example (Python/Flask)
```python
# VULNERABLE: No rate limiting on login
@app.route('/login', methods=['POST'])
def login():
    username = request.json['username']
    password = request.json['password']

    user = authenticate(username, password)
    if user:
        return {'token': generate_token(user)}
    return {'error': 'Invalid credentials'}, 401

# SECURE: Rate limiting with time window
from flask_limiter import Limiter

limiter = Limiter(app, key_func=lambda: request.remote_addr)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per 15 minutes")  # 5 attempts per 15-min window
def login():
    username = request.json['username']
    password = request.json['password']

    user = authenticate(username, password)
    if user:
        return {'token': generate_token(user)}
    return {'error': 'Invalid credentials'}, 401
```

### Detection
- Find authentication endpoints without rate limiting decorators
- Look for: `@limiter.limit`, `@RateLimit`, `RateLimiter`
- Check password reset, OTP generation, API endpoints

### Example (Express.js)
```javascript
// VULNERABLE: No rate limiting
app.post('/api/reset-password', async (req, res) => {
  const { email } = req.body;
  await sendPasswordResetEmail(email);
  res.json({ message: 'Reset email sent' });
});

// SECURE: Rate limiting
const rateLimit = require('express-rate-limit');

const resetLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 3, // 3 requests per window
  message: 'Too many reset requests'
});

app.post('/api/reset-password', resetLimiter, async (req, res) => {
  const { email } = req.body;
  await sendPasswordResetEmail(email);
  res.json({ message: 'Reset email sent' });
});
```

## Pattern 4: Client-Side Expiration Only

### Description
Expiration checked only on client side, not enforced server-side.

### Example (JavaScript + Python)
```javascript
// VULNERABLE: Client-side expiration check
function isTokenValid(token) {
  const decoded = JSON.parse(atob(token.split('.')[1]));
  return Date.now() < decoded.exp * 1000;  // Client-side only!
}

// Server doesn't check expiration
@app.route('/api/data')
def get_data():
    token = request.headers.get('Authorization')
    decoded = jwt.decode(token, options={"verify_signature": False})
    # Missing: Expiration verification
    return get_user_data(decoded['user_id'])
```

### Detection
- Find JWT decode with `verify=False` or `verify_signature=False`
- Look for expiration checks in frontend code without backend validation
- Check if server validates `exp` claim

## Pattern 5: Race Conditions in Expiration

### Description
Time-of-check to time-of-use (TOCTOU) vulnerabilities in expiration checks.

### Example (Python)
```python
# VULNERABLE: Race condition
def use_token(token):
    if is_token_expired(token):  # Check time
        raise TokenExpiredError()

    # Time passes here - token might expire
    time.sleep(0.1)  # Simulating processing

    # Use token - might be expired now
    return perform_action(token)  # Use time

# SECURE: Single atomic check
def use_token(token):
    # Check and use in single operation
    decoded = jwt.decode(token, SECRET_KEY)  # Checks exp atomically
    return perform_action(decoded)
```

### Detection
- Look for separate expiration check and token use
- Check for time gaps between validation and use
- Look for: check → delay → use patterns

## Pattern 6: Hardcoded Timeouts

### Description
Security timeouts hardcoded without configuration or documentation.

### Example (Python)
```python
# VULNERABLE: Magic number timeout
def create_session(user):
    session_id = generate_session_id()
    expiry = datetime.now() + timedelta(seconds=86400)  # What is 86400?
    save_session(session_id, user, expiry)
    return session_id

# SECURE: Named constant with documentation
# Session timeout: 24 hours for general users
SESSION_TIMEOUT_SECONDS = 24 * 60 * 60

def create_session(user):
    session_id = generate_session_id()
    expiry = datetime.now() + timedelta(seconds=SESSION_TIMEOUT_SECONDS)
    save_session(session_id, user, expiry)
    return session_id
```

### Detection
- Look for magic numbers in time calculations
- Check for: `3600`, `86400`, `604800` (common second values)
- Verify timeouts are configurable and documented

## Pattern 7: Inconsistent Timeout Enforcement

### Description
Different parts of the system enforce different timeouts for the same resource.

### Example (Microservices)
```python
# Service A: 30-minute session timeout
SESSION_TIMEOUT = timedelta(minutes=30)

# Service B: 1-hour session timeout (inconsistent!)
SESSION_TIMEOUT = timedelta(hours=1)

# Service C: No timeout check at all
def validate_session(session_id):
    session = get_session(session_id)
    return session is not None  # Missing expiration check
```

### Detection
- Search for timeout constants across codebase
- Compare values for same resource type
- Check if all services validate expiration

## Pattern 8: No Cleanup of Expired Data

### Description
Expired tokens/sessions remain in database without cleanup.

### Example (Python)
```python
# VULNERABLE: Expired tokens accumulate
def validate_reset_token(token):
    record = db.query(ResetToken).filter_by(token=token).first()
    if not record:
        return None
    if datetime.now() > record.expires_at:
        return None  # Expired but not deleted
    return record.user_id

# SECURE: Clean up expired tokens
def validate_reset_token(token):
    # Delete expired tokens first
    db.query(ResetToken).filter(
        ResetToken.expires_at < datetime.now()
    ).delete()
    db.commit()

    record = db.query(ResetToken).filter_by(token=token).first()
    if not record:
        return None
    return record.user_id

# Better: Scheduled cleanup job
def cleanup_expired_tokens():
    """Run periodically (e.g., hourly cron job)"""
    db.query(ResetToken).filter(
        ResetToken.expires_at < datetime.now()
    ).delete()
    db.commit()
```

### Detection
- Check if expired records are deleted
- Look for cleanup jobs or TTL indexes
- Verify database doesn't accumulate expired data

## Pattern 9: Timezone Issues

### Description
Mixing timezone-aware and timezone-naive datetimes causes incorrect expiration.

### Example (Python)
```python
# VULNERABLE: Timezone confusion
def create_token(user):
    # Server in UTC, but using local time
    expiry = datetime.now() + timedelta(hours=1)  # Local time!
    token = jwt.encode({
        'user_id': user.id,
        'exp': expiry.timestamp()  # Converted to UTC
    }, SECRET_KEY)
    return token

# SECURE: Always use UTC
def create_token(user):
    expiry = datetime.utcnow() + timedelta(hours=1)  # UTC
    token = jwt.encode({
        'user_id': user.id,
        'exp': expiry.timestamp()
    }, SECRET_KEY)
    return token

# Better: Use timezone-aware datetimes
from datetime import timezone

def create_token(user):
    expiry = datetime.now(timezone.utc) + timedelta(hours=1)
    token = jwt.encode({
        'user_id': user.id,
        'exp': expiry.timestamp()
    }, SECRET_KEY)
    return token
```

### Detection
- Look for `datetime.now()` vs `datetime.utcnow()`
- Check for timezone-naive datetime usage
- Verify consistent timezone handling

## Pattern 10: Timing Attack Vulnerabilities

### Description
Non-constant-time comparisons leak timing information.

### Example (Python)
```python
# VULNERABLE: Timing attack on token comparison
def validate_token(provided_token, stored_token):
    if provided_token == stored_token:  # Early exit leaks info
        return True
    return False

# SECURE: Constant-time comparison
import hmac

def validate_token(provided_token, stored_token):
    return hmac.compare_digest(provided_token, stored_token)
```

### Detection
- Look for direct string comparison of secrets
- Check for: `==`, `!=` on tokens, passwords, signatures
- Verify use of constant-time comparison functions

## Detection Checklist

For each security-critical operation:

- [ ] Is there an expiration time set?
- [ ] Is the expiration time checked before use?
- [ ] Is the expiration time reasonable for the use case?
- [ ] Is expiration enforced server-side?
- [ ] Are there race conditions between check and use?
- [ ] Is the timeout configurable and documented?
- [ ] Is timeout enforcement consistent across services?
- [ ] Are expired records cleaned up?
- [ ] Are timezones handled correctly?
- [ ] Are comparisons constant-time where needed?

