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.
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:
- Un informe válido. El manifiesto sobrevivió. Bien.
- Manifiesto no encontrado. Algo en su pipeline lo eliminó. Este es el caso común.
- Un error de validación. Hay un manifiesto presente, pero su firma ya no coincide con los bytes. Algo modificó el archivo y dejó el manifiesto antiguo adjunto, lo cual es peor que eliminarlo, porque parece manipulación para cualquier verificador posterior.
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
- Cuerpo:
multipart/form-datacon su fixture firmado adjunto. La mecánica es la misma que en la prueba de APIs de carga de archivos. - Aserciones: el estado es
201, y la respuesta coincide con su esquema. - Script post-respuesta para pasar la URL al siguiente paso:
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}}
- Aserciones: el estado es
200,Content-Typees el formato que espera, y el tamaño del cuerpo es cercano al que subió. Una caída drástica del tamaño es un fuerte indicio de que el archivo fue recodificado.
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.
- Archivo firmado válido. Se espera
verified. Detecta la eliminación excesivamente entusiasta. - Archivo sin metadatos. Misma imagen, manifiesto eliminado con
exiftool -all=. Se esperaabsent, no un error y definitivamente noverified. - 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. - Formato no soportado. Algo sin soporte de manifiesto en absoluto. Se espera un
absentlimpio 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.
- La afirmación de su producto. Si su interfaz de usuario muestra un distintivo de procedencia, y su pipeline elimina los manifiestos, el distintivo es incorrecto para cada activo que pasó por un redimensionamiento. Eso es un problema de confianza del que se enterará por un usuario.
- Su historia de cumplimiento. Si depende de C2PA para algo relacionado con el Artículo 50, un manifiesto eliminado es un control que no funciona. La división entre proveedor y desplegador en el Artículo 50 de la Ley de IA de la UE para desarrolladores de API explica qué deberes le corresponden realmente.
- La señal en sí misma. La procedencia solo funciona si la cadena se mantiene de principio a fin. Cada pipeline que elimina silenciosamente los manifiestos hace que todo el ecosistema sea menos útil, incluso para usted cuando intenta verificar algo.
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
- ¿El redimensionamiento de una imagen elimina los metadatos C2PA? Sí, por defecto en cada biblioteca común. Preservar el bloque de metadatos requiere una bandera explícita, y mantener una firma válida requiere volver a firmar la salida transformada.
- ¿Cómo verifico si un archivo tiene metadatos C2PA? Ejecute
c2patool <file>desde la línea de comandos, o arrastre el archivo a la página de verificación de Content Credentials. - ¿Puedo mantener los metadatos C2PA al redimensionar? Sí, pero no solo conservándolos. Conserve el bloque, luego vuelva a firmar la salida con una aserción de acción como
c2pa.resizedutilizando una de las bibliotecasc2pa. De lo contrario, la firma antigua ya no coincidirá con los nuevos bytes. - ¿Los CDN eliminan las Credenciales de Contenido? Muchos lo hacen cuando auto-optimizan. Algunos ahora las preservan y vuelven a firmar de forma nativa. Pruebe a través de la URL de entrega que sus usuarios visitan, no a través del origen.
- ¿Cuál es la diferencia entre un manifiesto eliminado y uno inválido? Eliminado significa que no se encontró ningún manifiesto, lo que no le dice nada sobre el origen del archivo. Inválido significa que existe un manifiesto pero su firma no coincide con los bytes, lo que significa que el archivo cambió después de la firma. Manténgalos como estados separados.
- ¿Puede Apidog verificar una firma C2PA directamente? Orquesta el viaje de ida y vuelta y realiza aserciones sobre las respuestas HTTP, incluyendo el JSON de un endpoint de verificación. El análisis de la firma en sí es trabajo de
c2patool, ejecutado como un paso de CI o dentro de su propio servicio. Utilice ambos juntos. - ¿Debo eliminar EXIF por privacidad pero mantener C2PA? Ese es el objetivo correcto y requiere un enfoque selectivo. Un
-stripgeneral los elimina a ambos. Elimine los bloques EXIF que le interesan específicamente y deje el manifiesto C2PA intacto.
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.
