ESLint InterlaceESLint Interlace
Plugin: node-securityRules

no-timing-unsafe-compare

ESLint rule documentation for no-timing-unsafe-compare

📡 Live from GitHub — This documentation is fetched directly from no-timing-unsafe-compare.md and cached for 6 hours.

Keywords: timing attack, constant-time, timingSafeEqual, secret comparison, CWE-208, security, ESLint rule CWE: CWE-208
OWASP: A02:2021-Cryptographic Failures

Disallow timing-unsafe comparison of secrets

Detects timing-unsafe comparison of secrets using === or == operators. This rule is part of eslint-plugin-node-security and provides LLM-optimized error messages with fix suggestions.

🚨 Security rule | 💡 Provides suggestions | ⚠️ Set to error in recommended

Quick Summary

AspectDetails
CWE ReferenceCWE-208 (Timing Attack)
SeverityHigh (security vulnerability)
Auto-Fix💡 Suggests crypto.timingSafeEqual()
CategorySecurity
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForNode.js applications comparing tokens, secrets, or signatures

Vulnerability and Risk

Vulnerability: Using === to compare secrets enables timing attacks. The comparison short-circuits on the first mismatched character, so the time taken reveals information about how many characters matched.

Risk: An attacker can measure comparison times to guess secret values character-by-character. For example, comparing API keys or HMAC signatures with === allows attackers to brute-force the correct value.

Rule Details

This rule detects ===, ==, !==, and != comparisons where at least one operand has a name suggesting it's a secret (token, password, key, secret, hash, signature, etc.).

Why This Matters

RiskImpactSolution
⏱️ Timing LeakComparison time reveals match lengthUse crypto.timingSafeEqual()
🔑 Secret Brute ForceAttack one character at a timeConstant-time comparison
🔒 Token BypassForge tokens by timing analysisNever use === for secrets

Configuration

OptionTypeDefaultDescription
secretPatternsstring[]['token', 'secret', 'key', ...]Variable name patterns for secrets
{
  rules: {
    'node-security/no-timing-unsafe-compare': ['error', {
      secretPatterns: [
        'token', 'secret', 'key', 'password', 'hash', 'signature',
        'mac', 'hmac', 'digest', 'apiKey', 'api_key', 'auth',
        'credential', 'bearer', 'jwt', 'csrf', 'nonce'
      ]
    }]
  }
}

Examples

❌ Incorrect

// Timing-unsafe comparison of API key - HIGH risk
function validateApiKey(userKey: string, storedKey: string) {
  return userKey === storedKey; // ❌ Leaks timing information
}

// Comparing tokens with ===
if (submittedToken === validToken) {
  // ❌ Vulnerable
  grantAccess();
}

// HMAC verification with ===
const expectedHash = crypto.createHmac('sha256', secret).update(data).digest();
if (receivedHash === expectedHash) {
  // ❌ Timing attack possible
  processData();
}

✅ Correct

import crypto from 'crypto';

// Constant-time comparison
function validateApiKey(userKey: string, storedKey: string) {
  const userBuffer = Buffer.from(userKey);
  const storedBuffer = Buffer.from(storedKey);

  // Lengths must match for timingSafeEqual
  if (userBuffer.length !== storedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(userBuffer, storedBuffer); // ✅ Safe
}

// HMAC verification (constant-time)
function verifyHmac(data: Buffer, receivedHmac: Buffer, secret: Buffer) {
  const expectedHmac = crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest();

  if (receivedHmac.length !== expectedHmac.length) {
    return false;
  }

  return crypto.timingSafeEqual(receivedHmac, expectedHmac); // ✅ Safe
}

// JWT verification (use library)
import jwt from 'jsonwebtoken';
jwt.verify(token, secret); // ✅ Library handles timing-safe comparison

How Timing Attacks Work

Secret:   "s3cr3t_k3y"
Guess 1:  "aaaaaaaaaa" → Fast rejection (0 chars match)
Guess 2:  "saaaaaaaaa" → Slightly slower (1 char matches)
Guess 3:  "s3aaaaaaaa" → Even slower (2 chars match)
...
Guess N:  "s3cr3t_k3y" → Slowest (all chars match)

Each additional matching character adds measurable time, allowing attackers to discover secrets character-by-character.

Security Impact

VulnerabilityCWEOWASPCVSSImpact
Timing Discrepancy208A02:20215.9 MediumSecret value leak
Observable Timing208A02:20215.3 MediumBrute force enablement

Migration Guide

Phase 1: Discovery

{
  rules: {
    'node-security/no-timing-unsafe-compare': 'warn'
  }
}

Phase 2: Replacement

// Replace === with timingSafeEqual
if (userToken === validToken)  // ❌ Before
if (crypto.timingSafeEqual(Buffer.from(userToken), Buffer.from(validToken)))  // ✅ After

Phase 3: Enforcement

{
  rules: {
    'node-security/no-timing-unsafe-compare': 'error'
  }
}

Known False Negatives

Non-Standard Variable Names

Why: Only configured patterns are detected.

// ❌ NOT DETECTED - unusual variable name
if (userValue === dbValue) { ... }  // Actually comparing tokens

Mitigation: Add patterns to configuration or use consistent naming.

Indirect Comparisons

Why: Cross-function data flow not tracked.

// ❌ NOT DETECTED
function compare(a, b) {
  return a === b;
}
compare(userToken, validToken);

Mitigation: Search codebase for comparison patterns.

Further Reading

On this page

No Headings