HIGH timing attackbearer tokens

Timing Attack with Bearer Tokens

How Timing Attack Manifests in Bearer Tokens

When an API validates a Bearer token, many developers compare the supplied token with the expected value using a simple equality check (e.g., === in JavaScript, == in Python, or .equals() in Java). These operators short‑circuit: they stop comparing as soon as they find a mismatching character. An attacker who can measure the response time of successive guesses can infer how many leading characters matched, gradually rebuilding a valid token. This is a classic timing side‑channel that targets the authentication step of Bearer token handling.

Typical vulnerable code paths look like this:

// Node.js/Express – vulnerable
app.get('/protected', (req, res) => {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) {
    return res.status(401).send('Missing token');
  }
  const token = auth.substring(7);
  if (token === process.env.API_TOKEN) {   // ← non‑constant‑time compare
    return res.send('OK');
  }
  return res.status(403).send('Invalid token');
});

The same pattern appears in Python Flask, Java Spring, and Go net/http handlers when the token is compared with ==, equals, or simple slice equality.

Bearer Tokens-Specific Detection

Detecting a timing leak in Bearer token validation requires observing how response time changes with different token prefixes. middleBrick’s unauthenticated black‑box scan includes a timing‑check module that:

  • Sends a series of Bearer tokens that share a common prefix and differ in the next character (e.g., Bearer AAAAx…, Bearer AAABy…, …).
  • Measures the latency of each request with high‑resolution timers.
  • Looks for statistically significant differences that indicate a short‑circuit comparison.
  • Flags the endpoint when the variance exceeds a threshold derived from baseline noise.

Because the scan works without any agents or credentials, it can be run against a staging or production URL simply by pasting the endpoint into the middleBrick dashboard, CLI, or GitHub Action. The resulting report lists the finding under the “Authentication” category, includes the measured timing delta, and provides remediation guidance.

Example of a middleBrick CLI trigger:

middlebrick scan https://api.example.com/resource --output json

The JSON output will contain a field like:

{
  "check": "timing_attack_bearer_token",
  "severity": "medium",
  "description": "Token comparison uses non‑constant‑time equality, enabling timing side‑channel.",
  "remediation": "Use constant‑time comparison functions such as crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python)."
}

Bearer Tokens-Specific Remediation

Fixing the issue means replacing the naïve equality with a constant‑time comparison that always runs in the same amount of time regardless of where the first mismatch occurs.

Node.js (crypto.timingSafeEqual):

const crypto = require('crypto');

app.get('/protected', (req, res) => {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) {
    return res.status(401).send('Missing token');
  }
  const token = Buffer.from(auth.substring(7), 'utf8');
  const expected = Buffer.from(process.env.API_TOKEN, 'utf8');
  if (crypto.timingSafeEqual(token, expected)) {
    return res.send('OK');
  }
  return res.status(403).send('Invalid token');
});

Python (hmac.compare_digest):

import hmac
from flask import Flask, request, Response

app = Flask(__name__)
API_TOKEN = b's3cr3t'

@app.route('/protected')
def protected():
    auth = request.headers.get('Authorization')
    if not auth or not auth.startswith('Bearer '):
        return Response('Missing token', status=401)
    token = auth[7:].encode('utf-8')
    if hmac.compare_digest(token, API_TOKEN):
        return Response('OK')
    return Response('Invalid token', status=403)

Java (using Bouncy Castle’s constant‑time array comparison):

import org.bouncycastle.util.Arrays;

boolean constantTimeEquals(byte[] a, byte[] b) {
    return Arrays.constantTimeAreEqual(a, b);
}

// In a Spring filter
String auth = request.getHeader("Authorization");
if (auth == null || !auth.startsWith("Bearer ")) {
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing token");
    return;
}
String tokenStr = auth.substring(7);
if (!constantTimeEquals(tokenStr.getBytes(StandardCharsets.UTF_8),
                         EXPECTED_TOKEN.getBytes(StandardCharsets.UTF_8))) {
    response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid token");
    return;
}
// proceed

By using these constant‑time primitives, the comparison no longer leaks timing information, and the Bearer token validation step becomes resistant to this side‑channel.

Frequently Asked Questions

What is the main risk of a timing attack on Bearer token validation?
An attacker can measure subtle differences in response time to guess valid token characters, eventually extracting a working token and gaining unauthorized access to the API.
How does middleBrick help identify this issue without requiring agents or credentials?
middleBrick’s unauthenticated black‑box scan sends crafted Bearer tokens with varying prefixes, measures request latency, and flags endpoints where timing differences reveal a non‑constant‑time token comparison.