Tu API Elimina Metadatos C2PA: Cómo Detectarlo con una Prueba

Claude, Gemini y OpenAI ahora adjuntan manifiestos C2PA firmados a los archivos generados, así que la procedencia real está llegando a su extremo de carga y su pipeline probablemente la está borrando. Un curl de dos minutos lo prueba, y una prueba de tres capas lo mantiene solucionado.

Ashley Innocent

Ashley Innocent

12 August 2026

Tu API Elimina Metadatos C2PA: Cómo Detectarlo con una Prueba

Apidog para empresas

Despliegue local

SSO & RBAC

Conforme con SOC 2

Explorar Apidog Enterprise

Claude ahora adjunta metadatos de procedencia C2PA firmados a los archivos que genera. También lo hacen los modelos de imagen de OpenAI, y también Gemini. Esto significa que una señal de procedencia real está llegando a su punto final de carga por primera vez, y hay una buena probabilidad de que su pipeline lo esté eliminando antes de que alguien lo vea.

No maliciosamente. Por defecto. sharp().resize() produce un archivo limpio sin metadatos a menos que se especifique lo contrario. Lo mismo ocurre con ImageMagick. También con Pillow. Y con la mayoría de los CDN de imágenes. El manifiesto entra, sale un JPEG más pequeño y nada en sus registros lo menciona.

Este es un fallo comprobable, y la prueba no es complicada. Así es como los metadatos mueren en un pipeline normal, cómo probar que está sucediendo y cómo integrar una verificación de ida y vuelta en CI para que no vuelva a ocurrir. Apidog se encarga de la orquestación; c2patool se encarga de la verificación a nivel de bytes.

botón

Qué es lo que realmente se destruye

Un manifiesto C2PA es un bloque firmado criptográficamente incrustado en el contenedor del archivo. Registra quién firmó el activo y qué se afirmó sobre él, y debido a que está firmado, alterar los bytes sin volver a firmar rompe la firma de una manera que cualquier lector puede detectar.

El nivel del contenedor es la frase clave. Reescriba el contenedor y el manifiesto desaparecerá.

Operación ¿El manifiesto sobrevive por defecto?
Copia o movimiento byte a byte Sí
sharp().resize().toBuffer() No
ImageMagick convert / magick No
Pillow Image.save() No
PNG a WebP, JPEG a AVIF No
Auto-optimización de CDN de imágenes Usualmente no
Captura de pantalla No
Volver a guardar desde un editor de imágenes No
Subida a S3 sin transformación Sí

Todo lo que aparece en la columna "no" es algo que una aplicación web normal hace con cada imagen que acepta. Miniaturas, variantes responsivas, negociación de formato, limpieza de EXIF por privacidad. Cada una es razonable de forma aislada, y cada una termina silenciosamente la cadena de procedencia.

Cabe destacar: el hábito de -strip motivado por la privacidad es a menudo deliberado, porque EXIF contiene coordenadas GPS y números de serie de cámaras. Eliminar todos los metadatos para borrar los datos de ubicación también elimina el manifiesto de procedencia. Esos dos objetivos ahora entran en conflicto, y resolverlo significa ser selectivo en lugar de eliminar todo el bloque.

Pruébelo en dos minutos

Antes de construir cualquier cosa, confirme que tiene el problema. Necesita un archivo con un manifiesto válido. Cualquier imagen que genere Claude funciona, o puede tomar una muestra firmada de la Content Authenticity Initiative.

Instale la CLI de referencia:

cargo install c2patool

Verifique que el fixture esté realmente firmado:

c2patool fixtures/signed-sample.png

Debería obtener un informe JSON que nombre el generador de la reclamación y el estado de la firma. Ahora páselo por su propio stack y verifique el otro extremo:

# Subir a través de su endpoint real
curl -sS -X POST https://api.example.com/v1/assets \
  -H "Authorization: Bearer $API_TOKEN" \
  -F "file=@fixtures/signed-sample.png" \
  -o /tmp/upload.json

# Recuperarlo a través de la URL que usaría su frontend
ASSET_URL=$(jq -r '.url' /tmp/upload.json)
curl -sS "$ASSET_URL" -o /tmp/roundtrip.png

# ¿Sobrevivió el manifiesto?
c2patool /tmp/roundtrip.png

Tres posibles resultados, y significan cosas diferentes:

Ese tercer resultado es el que hay que buscar. Normalmente significa que una biblioteca de transformación preservó el bloque de metadatos mientras reescribía los píxeles.

Encuentre el paso que lo hace

Si el viaje de ida y vuelta falla, divida el pipeline. Verifique el manifiesto inmediatamente después de cada etapa en lugar de adivinar.

Sospechosos típicos en orden de probabilidad:

1. El paso de redimensionamiento o miniatura. El culpable más probable. En sharp, los metadatos se eliminan a menos que los conserve explícitamente:

// Elimina el manifiesto C2PA
await sharp(input).resize(1200).toFile(output);

// Preserva el bloque de metadatos
await sharp(input).resize(1200).keepMetadata().toFile(output);

Preservar el bloque es necesario pero no suficiente. Los píxeles cambiaron, por lo que la firma original ya no se valida con los nuevos bytes. Para mantener una cadena de procedencia funcional, debe volver a firmar la salida y registrar la transformación como una aserción de acción, típicamente c2pa.resized. Las bibliotecas c2pa para Rust, Python, JavaScript y C admiten esto.

2. Conversión de formato. Servir AVIF o WebP significa un nuevo contenedor. Misma regla: preservar y volver a firmar, o aceptar que la cadena termina ahí y decirlo.

3. El CDN. Muchos CDN de imágenes reescriben en el momento de la entrega. Algunos ahora preservan y vuelven a firmar las Credenciales de Contenido de forma nativa; la mayoría históricamente las eliminaron. Pruebe a través de la URL de entrega que sus usuarios realmente visitan, no a través del origen, o obtendrá un resultado verde que no significa nada.

4. Normalización de carga. Los servicios que recodifican en la ingesta para estandarizar formatos son fáciles de olvidar, porque el código reside en un repositorio de infraestructura que nadie lee.

Conviértalo en una prueba permanente

Un curl único prueba el estado actual. No impide que alguien añada un paso de redimensionamiento en el próximo sprint. La verificación debe vivir en CI.

Divídalo en dos capas, porque dos herramientas diferentes son buenas para dos cosas diferentes.

Capa uno: el viaje de ida y vuelta, en Apidog

La orquestación es una prueba de API encadenada normal: cargar un fixture, capturar la URL devuelta, recuperar el activo a través de la ruta de entrega real, afirmar lo que se devuelve.

En Apidog, este es un escenario de prueba con dos pasos.

Paso 1: POST /v1/assets

const body = pm.response.json();
pm.environment.set("ASSET_URL", body.url);
pm.test("upload returns a delivery URL", function () {
    pm.expect(body.url).to.be.a("string").and.to.include("https://");
});

Paso 2: GET {{ASSET_URL}}

const uploadedBytes = Number(pm.environment.get("FIXTURE_BYTES"));
const returnedBytes = pm.response.responseSize;
pm.test("asset was not silently re-encoded", function () {
    pm.expect(returnedBytes).to.be.above(uploadedBytes * 0.9);
});

El tamaño es una heurística, no una prueba. Captura los fallos evidentes de forma económica y se ejecuta en la misma suite que todo lo demás. Los patrones de aserción estándar se cubren en asserción de APIs.

Capa dos: la verificación a nivel de bytes, en CI

Verificar una firma significa analizar el contenedor, lo cual es trabajo de c2patool, no de un cliente HTTP. Ejecútelo como un paso del pipeline contra el archivo que el viaje de ida y vuelta recuperó:

# .github/workflows/provenance.yml
name: provenance
on: [pull_request]

jobs:
  c2pa-round-trip:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar c2patool
        run: cargo install c2patool

      - name: Instalar Apidog CLI
        run: npm install -g apidog-cli

      - name: Ejecutar el escenario de ida y vuelta
        run: |
          apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
            -t "$SCENARIO_ID" -e "$ENV_ID" -r cli,html --out-dir ./apidog-reports
        env:
          APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
          SCENARIO_ID: ${{ vars.PROVENANCE_SCENARIO_ID }}
          ENV_ID: ${{ vars.APIDOG_ENV_ID }}

      - name: Verificar que el manifiesto sobrevivió
        run: |
          set -euo pipefail
          curl -sS "$ASSET_URL" -o /tmp/roundtrip.png
          c2patool /tmp/roundtrip.png > /tmp/report.json
          jq -e '.validation_status == null or (.validation_status | length) == 0' /tmp/report.json

set -euo pipefail es importante. Sin él, c2patool fallando en un archivo sin metadatos produce una advertencia y una compilación exitosa, lo cual es el fallo exacto que intentaba prevenir. Si es nuevo en la ejecución de escenarios de Apidog en un pipeline, la automatización de pruebas de API en GitHub Actions cubre la configuración.

Capa tres, opcional: un endpoint de verificación

Si la procedencia es una característica del producto en lugar de una verificación interna, el diseño más limpio es un pequeño endpoint en su propio servicio que ejecute la biblioteca c2pa y devuelva un resultado estructurado. Así, todo es comprobable como JSON ordinario, y su frontend obtiene una respuesta real en lugar de una suposición.

{
  "asset_id": "img_9f2c41",
  "provenance": {
    "status": "verified",
    "standard": "c2pa",
    "signer": "Anthropic",
    "signature_valid": true,
    "checked_at": "2026-08-11T09:14:22Z",
    "tool": "c2patool/0.9"
  }
}

Mantenga tres estados, no dos. verified, absent e invalid significan cosas genuinamente diferentes, y colapsar absent e invalid en un solo booleano desecha la señal más interesante que tiene. Añada unchecked si su verificador puede no estar disponible, para que una interrupción no se disfrace de un resultado limpio.

Documente la forma en su definición OpenAPI y valide contra ella, para que los campos no desaparezcan en una refactorización. Cómo validar especificaciones OpenAPI cubre ese aspecto.

Los cuatro fixtures que vale la pena conservar

Una suite de procedencia necesita entradas deliberadamente defectuosas, no solo un camino feliz.

  1. Archivo firmado válido. Se espera verified. Detecta la eliminación excesivamente entusiasta.
  2. Archivo sin metadatos. Misma imagen, manifiesto eliminado con exiftool -all=. Se espera absent, no un error y definitivamente no verified.
  3. Archivo manipulado. Archivo firmado con un byte alterado después de la firma. Se espera invalid. Este es el que prueba que está verificando la firma en lugar de solo comprobar que existe un bloque.
  4. Formato no soportado. Algo sin soporte de manifiesto en absoluto. Se espera un absent limpio en lugar de un 500.

Suba los cuatro al repositorio junto al escenario de prueba. Son pequeños, nunca cambian, y son la diferencia entre una prueba que pasa y una prueba que significa algo.

Por qué molestarse

Tres razones, en orden ascendente de cuánto le costarán.

Descargue Apidog para construir el escenario de ida y vuelta contra sus propios endpoints, y luego integre el paso de c2patool detrás de él.

Preguntas frecuentes

La conclusión

Los metadatos de procedencia llegan a su API intactos y, por lo general, se pierden en el camino, y nada en su monitoreo se lo dirá. La solución es un fixture, un viaje de ida y vuelta a través de la ruta de entrega real, y una verificación con c2patool que falle la compilación.

Veinte minutos de configuración, y convierte una afirmación que está haciendo en su UI en una garantía que su pipeline realmente aplica.

botón

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs