DEV Community

Cover image for Votre API supprime les métadonnées C2PA : Comment le détecter avec un test
Antoine Laurent
Antoine Laurent

Posted on Originally published at apidog.com

Votre API supprime les métadonnées C2PA : Comment le détecter avec un test

Claude attache désormais des métadonnées de provenance C2PA signées aux fichiers qu'il génère. Il en va de même pour les modèles d'images d'OpenAI et, comme l'explique cet article sur Gemini. Pour la première fois, un véritable signal de provenance peut donc arriver jusqu'à votre point de terminaison de téléchargement. Pourtant, votre pipeline risque fortement de le supprimer avant qu'un utilisateur ne le voie.

Essayez Apidog dès aujourd’hui

Ce phénomène n'est généralement pas malveillant : il s'agit du comportement par défaut. sharp().resize() produit un fichier sans métadonnées, sauf si vous demandez explicitement leur conservation. ImageMagick et Pillow se comportent de manière similaire, tout comme la plupart des CDN d'images. Le manifeste entre, un JPEG plus petit ressort, et rien dans vos logs ne signale sa disparition.

Il s'agit d'une défaillance que vous pouvez tester rapidement. Voici comment les métadonnées disparaissent dans un pipeline courant, comment identifier l'étape responsable et comment ajouter une vérification de bout en bout dans la CI afin d'empêcher les régressions. Apidog orchestre le scénario HTTP ; c2patool vérifie le fichier au niveau des octets.

Ce qui est réellement détruit

Un manifeste C2PA est un bloc signé cryptographiquement, intégré dans le conteneur du fichier. Il indique qui a signé l'actif et ce qui est revendiqué à son sujet. Comme il est signé, toute modification des octets sans nouvelle signature peut être détectée par un lecteur compatible.

Le point important est le conteneur : dès que vous le réécrivez, le manifeste peut disparaître.

Opération Le manifeste survit-il par défaut ?
Copie ou déplacement bit-à-bit Oui
sharp().resize().toBuffer() Non
ImageMagick convert / magick Non
Pillow Image.save() Non
PNG vers WebP, JPEG vers AVIF Non
Optimisation automatique par CDN d'images Généralement non
Capture d'écran Non
Réenregistrement depuis un éditeur d'images Non
Téléchargement S3 sans transformation Oui

La plupart des opérations de la colonne « non » sont courantes dans une application web : création de miniatures, génération de variantes responsives, négociation de format ou suppression des métadonnées EXIF. Chacune est raisonnable isolément, mais chacune peut interrompre silencieusement la chaîne de provenance.

La suppression des métadonnées pour des raisons de confidentialité est souvent volontaire. Les données EXIF peuvent contenir des coordonnées GPS ou des numéros de série d'appareil photo. Cependant, une suppression globale avec -strip élimine également le manifeste de provenance. Il faut donc supprimer sélectivement les blocs EXIF concernés au lieu de supprimer toutes les métadonnées.

Prouvez-le en deux minutes

Avant de modifier votre pipeline, confirmez le problème avec un fichier signé. Une image générée par Claude convient, ou vous pouvez utiliser un échantillon signé fourni par l'Initiative pour l'Authenticité du Contenu.

Installez l'outil de ligne de commande de référence :

cargo install c2patool
Enter fullscreen mode Exit fullscreen mode

Vérifiez ensuite le fichier original :

c2patool fixtures/signed-sample.png
Enter fullscreen mode Exit fullscreen mode

Le résultat devrait contenir un rapport JSON indiquant le générateur de la revendication et le statut de la signature.

Faites maintenant passer le fichier par votre véritable parcours d'upload et de livraison :

# Téléchargement via votre véritable point de terminaison
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

# Récupération via l'URL utilisée par votre frontend
ASSET_URL=$(jq -r '.url' /tmp/upload.json)
curl -sS "$ASSET_URL" -o /tmp/roundtrip.png

# Le manifeste a-t-il survécu ?
c2patool /tmp/roundtrip.png
Enter fullscreen mode Exit fullscreen mode

Interprétez le résultat selon trois cas :

  • Rapport valide : le manifeste a survécu et sa signature est valide.
  • Aucun manifeste trouvé : une étape du pipeline l'a supprimé. C'est le cas le plus courant.
  • Erreur de validation : un manifeste est présent, mais sa signature ne correspond plus aux octets du fichier. Une transformation a probablement modifié le fichier en conservant l'ancien manifeste. Ce cas est plus grave qu'une simple suppression, car il signale une altération aux vérificateurs en aval.

Le troisième résultat apparaît généralement lorsqu'une bibliothèque conserve le bloc de métadonnées tout en réécrivant les pixels.

Identifiez l'étape responsable

Si le test de bout en bout échoue, vérifiez le fichier après chaque étape du pipeline. Ne vous limitez pas au fichier présent dans le stockage d'origine : testez aussi l'URL de livraison réellement utilisée par vos utilisateurs.

1. Redimensionnement et génération de miniatures

C'est le premier suspect. Dans sharp, les métadonnées sont supprimées par défaut :

// Supprime le manifeste C2PA
await sharp(input).resize(1200).toFile(output);

// Préserve le bloc de métadonnées
await sharp(input)
  .resize(1200)
  .keepMetadata()
  .toFile(output);
Enter fullscreen mode Exit fullscreen mode

Conserver le bloc ne suffit toutefois pas à conserver une signature valide. Les pixels ont changé ; la signature originale ne correspond donc plus aux nouveaux octets.

Pour maintenir une chaîne de provenance valide, vous devez :

  1. conserver le bloc de métadonnées ;
  2. re-signer la sortie ;
  3. enregistrer la transformation comme une assertion d'action, par exemple c2pa.resized.

Les bibliothèques c2pa pour Rust, Python, JavaScript et C prennent en charge ce type d'intégration.

2. Conversion de format

Convertir une image vers AVIF ou WebP crée un nouveau conteneur. Vous devez alors préserver et re-signer la sortie, ou accepter que la chaîne de provenance s'arrête à cette étape et le signaler explicitement.

3. CDN d'images

De nombreux CDN réécrivent les fichiers au moment de la livraison. Certains conservent et re-signent désormais les Content Credentials nativement, mais ce n'était historiquement pas le cas de la plupart d'entre eux.

Testez toujours l'URL de livraison finale :

curl -sS "$DELIVERY_URL" -o /tmp/delivered-image
c2patool /tmp/delivered-image
Enter fullscreen mode Exit fullscreen mode

Tester uniquement l'origine peut produire un résultat positif qui ne reflète pas le fichier réellement téléchargé par l'utilisateur.

4. Normalisation à l'ingestion

Les services qui réencodent les fichiers dès leur réception sont faciles à oublier. Cherchez ces transformations dans les dépôts d'infrastructure, les workers asynchrones et les fonctions déclenchées par l'upload.

Ajoutez un test permanent à la CI

Un curl ponctuel confirme uniquement l'état actuel du pipeline. Une modification future — par exemple l'ajout d'une étape de redimensionnement — peut réintroduire le problème.

Séparez la vérification en deux couches :

  1. un test de bout en bout pour valider le parcours HTTP ;
  2. une vérification au niveau des octets pour valider le manifeste C2PA.

Couche 1 : test de bout en bout dans Apidog

L'orchestration suit un scénario API classique :

  1. téléverser un fichier signé ;
  2. capturer l'URL renvoyée ;
  3. récupérer l'actif via le chemin de livraison réel ;
  4. vérifier la réponse HTTP et le fichier retourné.

Étape 1 : POST /v1/assets

Configurez une requête multipart/form-data contenant le fichier signé. La mécanique est la même que dans le test des API de téléchargement de fichiers.

Ajoutez les assertions suivantes :

  • le statut HTTP vaut 201 ;
  • la réponse respecte votre schéma ;
  • la réponse contient une URL de livraison.

Le script post-réponse peut transmettre l'URL à l'étape suivante :

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://");
});
Enter fullscreen mode Exit fullscreen mode

Étape 2 : GET {{ASSET_URL}}

Vérifiez :

  • le statut HTTP 200 ;
  • le Content-Type attendu ;
  • une taille de réponse cohérente avec le fichier envoyé.

Une diminution importante de la taille est un indice qu'un réencodage a eu lieu :

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);
});
Enter fullscreen mode Exit fullscreen mode

La taille est une heuristique, pas une preuve cryptographique. Elle permet néanmoins de détecter rapidement les échecs flagrants dans la même suite que vos autres tests. Les modèles d'assertion standard sont présentés dans le guide des assertions API.

Couche 2 : vérification au niveau des octets dans la CI

La validation d'une signature nécessite l'analyse du conteneur. C'est le rôle de c2patool, pas celui du client HTTP.

Exécutez l'outil sur le fichier récupéré par le scénario de bout en bout :

# .github/workflows/provenance.yml
name: provenance

on: [pull_request]

jobs:
  c2pa-round-trip:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install c2patool
        run: cargo install c2patool

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

      - name: Run the round-trip scenario
        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: Verify the manifest survived
        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
Enter fullscreen mode Exit fullscreen mode

set -euo pipefail est indispensable. Sans cette option, un échec de c2patool sur un fichier dépouillé pourrait être traité comme un avertissement, laissant la CI au vert.

Si vous débutez avec l'exécution de scénarios Apidog dans un pipeline, consultez le guide sur l'automatisation des tests API dans GitHub Actions.

Couche 3, optionnelle : un point de terminaison de vérification

Si la provenance est une fonctionnalité exposée par votre produit, créez un petit point de terminaison dans votre service. Il peut exécuter la bibliothèque c2pa et renvoyer un résultat structuré.

Vos tests HTTP pourront alors vérifier un JSON normal :

{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Conservez au moins trois états distincts :

  • verified : manifeste présent et signature valide ;
  • absent : aucun manifeste trouvé ;
  • invalid : manifeste présent, mais signature invalide.

Ajoutez unchecked si le vérificateur peut être temporairement indisponible. Une panne de vérification ne doit pas être interprétée comme un résultat propre.

Documentez cette structure dans votre définition OpenAPI et validez-la afin d'éviter la disparition de champs lors d'une refactorisation. Le guide Comment valider les spécifications OpenAPI couvre cette étape.

Conservez quatre fichiers de test

Une suite de tests de provenance doit couvrir les cas négatifs, pas uniquement le parcours nominal.

  1. Fichier signé valide : attendez verified. Ce test détecte les suppressions involontaires.
  2. Fichier dépouillé : utilisez la même image, mais supprimez le manifeste avec exiftool -all=. Attendez absent, et non une erreur ou verified.
  3. Fichier altéré : modifiez un octet après la signature. Attendez invalid. Ce test confirme que vous validez réellement la signature.
  4. Format non pris en charge : utilisez un fichier sans support de manifeste. Attendez un état absent propre plutôt qu'une erreur 500.

Commitez ces quatre fichiers dans le dépôt, à côté du scénario de test. Ils sont stables et permettent de détecter les régressions du pipeline.

Pourquoi cette vérification est importante

La promesse de votre produit

Si votre interface affiche un badge de provenance alors que votre pipeline supprime les manifestes après un redimensionnement, le badge est incorrect. Vous risquez de découvrir le problème uniquement lorsqu'un utilisateur le signale.

La conformité

Si vous utilisez C2PA pour répondre à des exigences liées à l'Article 50, un manifeste supprimé signifie qu'un contrôle ne fonctionne pas. La distinction entre fournisseur et déployeur dans l'Article 50 de l'EU AI Act pour les développeurs d'API aide à déterminer les obligations applicables.

La fiabilité du signal

La provenance n'est utile que si la chaîne est conservée de bout en bout. Chaque pipeline qui supprime silencieusement les manifestes réduit la valeur du signal pour l'ensemble de l'écosystème.

Téléchargez Apidog pour construire le scénario de test de bout en bout contre vos propres points de terminaison, puis ajoutez la vérification c2patool dans votre CI.

FAQ

Le redimensionnement d'une image supprime-t-il les métadonnées C2PA ?

Oui, par défaut dans les bibliothèques courantes. La conservation du bloc de métadonnées nécessite une option explicite. Pour conserver une signature valide après transformation, vous devez également re-signer la sortie.

Comment vérifier si un fichier contient des métadonnées C2PA ?

Exécutez la commande suivante :

c2patool <file>
Enter fullscreen mode Exit fullscreen mode

Vous pouvez aussi déposer le fichier sur la page de vérification des Content Credentials.

Puis-je conserver les métadonnées C2PA lors d'un redimensionnement ?

Oui, mais la conservation seule ne suffit pas. Préservez le bloc, puis re-signez la sortie avec une assertion d'action telle que c2pa.resized, en utilisant l'une des bibliothèques c2pa. Sinon, l'ancienne signature ne correspondra plus aux nouveaux octets.

Les CDN suppriment-ils les Content Credentials ?

Beaucoup les suppriment lors de l'optimisation automatique. Certains les conservent et les re-signent désormais nativement. Testez l'URL de livraison réellement utilisée par vos utilisateurs, et non uniquement l'origine.

Quelle est la différence entre un manifeste supprimé et un manifeste invalide ?

Un manifeste supprimé signifie qu'aucun manifeste n'a été trouvé. Cela ne fournit aucune information sur l'origine du fichier.

Un manifeste invalide existe, mais sa signature ne correspond plus aux octets. Le fichier a donc été modifié après la signature. Conservez ces deux états séparément.

Apidog peut-il vérifier directement une signature C2PA ?

Apidog orchestre le test de bout en bout et vérifie les réponses HTTP, notamment le JSON d'un point de terminaison de vérification. L'analyse de la signature elle-même relève de c2patool, exécuté dans la CI ou dans votre propre service. Utilisez les deux outils ensemble.

Dois-je supprimer les données EXIF pour protéger la confidentialité tout en conservant C2PA ?

C'est l'objectif recommandé, mais il nécessite une approche sélective. Un -strip général supprime les données EXIF et le manifeste C2PA. Supprimez uniquement les blocs EXIF nécessaires et laissez le manifeste C2PA intact.

À retenir

Les métadonnées de provenance peuvent arriver intactes à votre API, puis disparaître pendant le redimensionnement, la conversion de format, l'optimisation CDN ou la livraison. Votre supervision ne le détectera pas forcément.

La solution est simple à automatiser :

  1. conservez un fichier signé comme fixture ;
  2. testez le parcours complet via l'URL de livraison réelle ;
  3. vérifiez le fichier retourné avec c2patool ;
  4. faites échouer la CI lorsque le manifeste est absent ou invalide ;
  5. distinguez toujours les états verified, absent, invalid et, si nécessaire, unchecked.

Une vingtaine de minutes de configuration suffit pour transformer une promesse affichée dans votre interface en une garantie effectivement appliquée par votre pipeline.

Top comments (0)