Volver a la wiki

Prevención de timing attacks en comparaciones criptográficas

Concepto

Un timing attack es un ataque donde un adversario mide el tiempo de ejecución de una operación para inferir información (p.ej. una contraseña o clave API).

El problema

Comparación ingenua (==)

if token == expected:
    return True
return False

Riesgo: La comparación de strings en Python termina cuando encuentra el primer byte diferente:

Ataque:

for i in range(256):
    for j in range(256):
        token = bytes([i, j] + [0] * 30)  # Probar todos los valores
        t_start = time.perf_counter()
        response = request(token=token)
        t_end = time.perf_counter()
        if t_end - t_start > threshold:
            print(f"Byte {i},{j} es correcto (tarda más)")
            break

Midiendo el tiempo de respuesta, el atacante puede adivinar byte a byte qué es el token correcto.

La solución: hmac.compare_digest()

import hmac
if hmac.compare_digest(provided, expected):
    return True
return False

Cómo funciona:

RFC 5234 (HMAC) + CWE-208 (Information Exposure Through Timing Discrepancy) recomiendan esto.

En CreaRack Pro

Antes de Etapa 3 (vulnerable)

core/workspace_api.py (_check_key):

def _check_key(request) -> bool:
    if not WORKSPACE_API_KEY:
        return False
    return request.headers.get("X-Workspace-Key") == WORKSPACE_API_KEY  # ⚠️ Timing attack

config/urls.py (_InternalToolsAuth.authenticate):

if token == expected:  # ⚠️ Timing attack
    return token
return None

Después de Etapa 3 (mitigado)

core/workspace_api.py:

import hmac

def _check_key(request) -> bool:
    """Validate workspace API key from header (constant-time compare)."""
    if not WORKSPACE_API_KEY:
        return False
    provided = request.headers.get("X-Workspace-Key", "")
    return hmac.compare_digest(provided, WORKSPACE_API_KEY)  # ✅ Constant-time

config/urls.py:

import hmac as _hmac

class _InternalToolsAuth(HttpBearer):
    def authenticate(self, request, token):
        expected = (_os.environ.get("INTERNAL_TOOLS_TOKEN") or "").strip()
        if not expected or len(expected) < 32:
            return None
        if _hmac.compare_digest(token or "", expected):  # ✅ Constant-time
            return token
        return None

Impacto de seguridad

AspectoAntesDespués
Exposición a timing attacks✅ Vulnerable✅ Mitigado
Tiempo de comparaciónVariable (1-N bytes)Constante
OverheadNingunoMínimo (C-level)
CompatibilidadN/APython 3.3+ (stdlib)

Cuándo usar hmac.compare_digest()

✅ Siempre que compares:

❌ No necesario para:

Limitaciones

  1. Solo mitiga timing attacks

    • No protege contra ataques de fuerza bruta (iterate 2^128 claves)
    • La defensa complementaria es: tasa límite (rate limit), tiempos de espera exponencial
  2. Asume tokens de igual longitud

    hmac.compare_digest("abc", "abcdefgh")  # Devuelve False rápido (longitud diferente)
    • Solución: Validar longitud antes
  3. Depende del contexto

    # ❌ MAL: Timing attack aún posible en la rama principal
    if not token:
        return None
    return hmac.compare_digest(token, expected)
    
    # ✅ BIEN: Mismo tiempo de cómputo
    provided = request.headers.get("X-Workspace-Key", "")
    return hmac.compare_digest(provided or "", expected)

En CreaRack, se rellena con default "" para evitar esta rama.

Referencias

Véase también

Subir