Observable Timing Discrepancy
Description
Observable Timing Discrepancy is a vulnerability that occurs when two or more operations in a product require different amounts of time to complete, and these timing differences are observable to an actor and reveal security-relevant information about the system's state. This weakness is particularly critical in cryptographic contexts where attackers can infer secret key properties by measuring the time required for cryptographic operations. Common manifestations include string comparisons that terminate early upon finding a mismatch, authentication checks that take longer for valid users, and cryptographic operations whose timing varies based on key bits. Even microsecond-level differences can be exploited through statistical analysis of multiple measurements to extract complete secrets.
Risk
Observable timing discrepancies represent one of the most subtle yet powerful attack vectors against secure systems. In cryptographic implementations, timing variations during operations like modular exponentiation or comparison can leak complete private keys through statistical analysis. Authentication systems that validate passwords character-by-character enable attackers to determine correct credentials one character at a time, dramatically reducing the keyspace that must be searched. Remote timing attacks over networks remain feasible despite noise, as attackers can aggregate measurements across thousands of requests to achieve microsecond-level precision. The risk is amplified in cloud environments where attackers can achieve low-latency access to target systems, and in cryptographic applications where timing variations directly correlate with secret values.
Solution
Implement constant-time algorithms for all security-sensitive operations, ensuring execution time remains identical regardless of input values or secret data. Use cryptographic libraries that specifically provide constant-time implementations, such as those using bitwise operations instead of branching. Replace standard string comparison functions with constant-time comparison functions like hmac.compare_digest() in Python or crypto.timingSafeEqual() in Node.js. For password verification, always compute and compare hashes even when usernames don't exist. Add controlled random delays to operations where constant-time implementation is impractical, though this provides weaker protection. Disable high-resolution timing APIs where possible, and monitor for timing-based attacks through anomaly detection. Regularly audit code for timing vulnerabilities using static analysis tools designed for side-channel detection.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality, Access Control | Scope: Confidentiality, Access Control Attackers can measure timing differences to extract sensitive data and bypass protection mechanisms. Cryptographic keys, passwords, and other secrets can be recovered through statistical timing analysis, enabling unauthorized access. |
| Confidentiality | Scope: Confidentiality Observable timing discrepancies in cryptographic operations directly leak information about secret keys. Through careful measurement and statistical analysis, attackers can recover complete private keys, breaking the confidentiality guarantees of encryption systems. |
Example Code
Vulnerable Code (JavaScript/Node.js)
The following code demonstrates a vulnerable authentication system with multiple timing side channels:
const crypto = require('crypto');
class VulnerableAuth {
constructor() {
this.users = new Map();
}
// Vulnerable: Early termination reveals valid usernames
async authenticate(username, password) {
const user = this.users.get(username);
// Timing leak #1: Quick return for non-existent users
if (!user) {
return { success: false, error: 'Authentication failed' };
}
// Compute password hash for existing user only
// Timing leak #2: Hash computation only for valid users
const hash = crypto.createHash('sha256')
.update(password + user.salt)
.digest('hex');
// Timing leak #3: Standard string comparison
// Terminates early on first mismatch
if (hash === user.passwordHash) {
return { success: true, token: this.generateToken(user) };
}
return { success: false, error: 'Authentication failed' };
}
// Vulnerable HMAC verification
verifySignature(message, signature, secret) {
const expected = crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
// Timing leak: Character-by-character comparison
// Attacker can determine correct characters by timing
if (signature === expected) {
return true;
}
return false;
}
}
The code has three timing vulnerabilities: (1) returning immediately for non-existent users, (2) only computing password hashes for valid users, and (3) using standard string comparison that terminates early.
Fixed Code (JavaScript/Node.js)
const crypto = require('crypto');
class SecureAuth {
constructor() {
this.users = new Map();
// Pre-computed dummy values for timing consistency
this.dummySalt = crypto.randomBytes(16).toString('hex');
this.dummyHash = crypto.randomBytes(32).toString('hex');
}
async authenticate(username, password) {
const user = this.users.get(username);
// Fixed: Always retrieve values, using dummy for non-existent users
const salt = user ? user.salt : this.dummySalt;
const storedHash = user ? user.passwordHash : this.dummyHash;
// Fixed: Always compute hash regardless of user existence
const computedHash = crypto.createHash('sha256')
.update(password + salt)
.digest('hex');
// Fixed: Use constant-time comparison
const hashValid = crypto.timingSafeEqual(
Buffer.from(computedHash, 'hex'),
Buffer.from(storedHash, 'hex')
);
// Only succeed if user exists AND password is correct
const userExists = user !== undefined;
const authenticated = hashValid && userExists;
if (authenticated) {
return { success: true, token: this.generateToken(user) };
}
// Log internally but return generic error
this.logFailedAttempt(username, userExists);
return { success: false, error: 'Authentication failed' };
}
verifySignature(message, signature, secret) {
const expected = crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
// Fixed: Constant-time comparison prevents timing attacks
try {
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
} catch (e) {
// Handle length mismatch consistently
// Still return false but in constant time
return false;
}
}
// Additional protection: Rate limiting
checkRateLimit(username) {
// Implement exponential backoff for repeated failures
// Helps mitigate statistical timing attacks
}
logFailedAttempt(username, userExists) {
// Internal logging for security monitoring
// Never expose this information to clients
}
}
The fix uses crypto.timingSafeEqual() for all comparisons, performs password hashing regardless of user existence using dummy values, and ensures all code paths execute in constant time.
Exploited in the Wild
Apache Shiro HMAC Timing Attack (Apache Shiro, 2019)
CVE-2019-10071 affected Apache Shiro, a popular Java security framework. The vulnerability existed because Shiro compared HMAC signatures using Java's standard String.equals() method, which terminates comparison upon finding the first non-matching character. Attackers could exploit this timing discrepancy to forge valid authentication tokens by determining the correct signature one character at a time, completely bypassing the cryptographic protection.
OpenSSL CBC Padding Oracle (OpenSSL, 2003)
CVE-2003-0078 demonstrated a timing attack against OpenSSL's SSL implementation. The vulnerability occurred because the server skipped MAC computation for messages with invalid padding, causing measurable timing differences. Attackers could exploit this to decrypt SSL/TLS traffic by distinguishing between padding errors and MAC errors through timing analysis, leading to the Vaudenay padding oracle attack that affected secure communications worldwide.
Cisco Router Password Timing Attack (Cisco, 2014)
CVE-2014-0984 affected Cisco routers where password validation terminated early upon finding an incorrect character. Attackers could determine the correct password character-by-character by measuring response times - correct characters led to slightly longer processing as validation continued to the next character. This reduced password cracking from potentially years to minutes for administrative access.
Tools to Test/Exploit
-
time-based-username-enumeration — Ruby tool for exploiting timing-based username enumeration vulnerabilities in web applications.
-
dudect — Tool for detecting timing leaks in cryptographic code through statistical analysis of execution times.
-
ctgrind — Valgrind-based tool for checking constant-time implementations in C/C++ code by detecting branches on secret data.
CVE Examples
-
CVE-2019-10071 — Apache Shiro compared HMAC signatures using non-constant-time String.equals(), enabling signature forgery.
-
CVE-2014-0984 — Cisco router password validation terminated early on incorrect characters, enabling timing-based password recovery.
-
CVE-2003-0078 — OpenSSL skipped MAC computation for invalid padding, creating a timing oracle for decrypting SSL traffic.
-
CVE-2003-0637 — Application used shorter timeout for non-existent users, enabling username enumeration.
-
CVE-2005-0918 — Browser allowed JavaScript to detect file existence through load timing differences.
References
-
MITRE Corporation. "CWE-208: Observable Timing Discrepancy." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/208.html
-
Kocher, Paul C. "Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems." Advances in Cryptology—CRYPTO '96. https://www.paulkocher.com/doc/TimingAttacks.pdf
-
Brumley, David and Boneh, Dan. "Remote Timing Attacks are Practical." Computer Networks 48.5 (2005): 701-716.
-
Bernstein, Daniel J. "Cache-timing attacks on AES." 2005. https://cr.yp.to/antiforgery/cachetiming-20050414.pdf