Alejandría

Busca en Alejandría

Técnicas

Fuzz testing

Explora entradas inesperadas para encontrar fallos difíciles de prever.

01

En pocas palabras

Generar o modificar automáticamente muchas entradas, incluidas inesperadas o inválidas, para descubrir fallos y comportamientos anómalos.

Qué problema resuelve

Casos límite que las pruebas con ejemplos escritos manualmente suelen omitir, como bloqueos, errores de memoria o excepciones inesperadas.

Cuándo utilizarlo

Parsers, APIs, procesamiento de archivos y protocolos. Ejemplo: variar un JSON enviado a una API para encontrar entradas que provocan un fallo interno en lugar de un rechazo controlado.

02

De la idea a la práctica

Funcionamiento, ejemplos y decisiones para pasar al siguiente nivel.

Cómo funciona

Un fuzzer genera o modifica entradas y observa el programa. Los motores guiados por cobertura conservan casos que recorren código nuevo y los usan como base para nuevas mutaciones.

Ejemplo: probar un importador

Parte de archivos pequeños válidos e introduce campos ausentes, tamaños extremos, codificaciones inesperadas y estructuras incompletas. Observa excepciones, consumo de recursos y consistencia del resultado.

Cuando aparece un fallo, reduce la entrada hasta obtener el caso mínimo que lo reproduce. Corrige la causa y conserva ese caso como prueba de regresión.

Herramientas por lenguaje

Lenguaje Herramienta
C / C++ libFuzzer, AFL++, con AddressSanitizer y UBSan
Rust cargo fuzz (libFuzzer)
Go Fuzzing nativo (go test -fuzz)
Java / JVM Jazzer (@FuzzTest con JUnit 5)
Python Atheris; Hypothesis para pruebas basadas en propiedades
JavaScript Jazzer.js
APIs HTTP Schemathesis o RESTler a partir de OpenAPI

Ejemplo en Go

func FuzzParsearMonto(f *testing.F) {
    for _, semilla := range []string{"1500000", "1.500.000,50", "0", "-1", ""} {
        f.Add(semilla)
    }
    f.Fuzz(func(t *testing.T, entrada string) {
        monto, err := ParsearMonto(entrada)
        if err != nil {
            return // rechazar entradas inválidas es correcto
        }
        // Propiedades que siempre deben cumplirse
        if monto.Negativo() {
            t.Fatalf("monto negativo aceptado: %q", entrada)
        }
        if rt, _ := ParsearMonto(monto.String()); !rt.Igual(monto) {
            t.Fatalf("ida y vuelta inconsistente: %q → %v", entrada, monto)
        }
    })
}
go test -fuzz=FuzzParsearMonto -fuzztime=5m ./montos

Las entradas que provocan fallos se guardan en testdata/fuzz/ y pasan a ejecutarse como pruebas normales.

Fuzzing de APIs a partir del contrato

Herramientas como Schemathesis leen la especificación OpenAPI, generan peticiones válidas e inválidas y comprueban que la API no devuelve errores 500, que las respuestas cumplen el esquema y que los códigos son coherentes. Es una forma barata de encontrar validaciones ausentes y excepciones no controladas antes que un atacante.

Oráculos: cómo saber que algo falló

Oráculo Ejemplo
Fallo del proceso Pánico, excepción no controlada, error de memoria
Propiedad (invariante) El resultado nunca es negativo; ordenar es idempotente
Ida y vuelta parsear(serializar(x)) == x
Diferencial Dos implementaciones (vieja y nueva) dan el mismo resultado
Recursos Tiempo o memoria por encima de un límite

El diferencial es especialmente útil en migraciones: compara la librería antigua y la nueva con millones de entradas.

Dónde priorizar

Parsers de archivos y mensajes recibidos de terceros (extractos bancarios, XML de facturación, imágenes de documentos), deserialización, decodificadores, cálculos con aritmética delicada y cualquier código que procese entradas no confiables. Proyectos open source críticos pueden integrarse en OSS-Fuzz, que los ejecuta continuamente.

Criterios de implementación

Define qué comportamiento cuenta como fallo y restablece el estado entre ejecuciones. Limita recursos y tiempos de cada caso. Para parsers nativos, combinar fuzzing con sanitizadores facilita detectar errores de memoria que no siempre provocan un cierre inmediato.

Continúa en las fuentes

Documentación oficial para verificar detalles y explorar más.