DEV Community

Cover image for APIによるC2PAメタデータ削除をテストで検知する方法
Akira
Akira

Posted on Originally published at apidog.com

APIによるC2PAメタデータ削除をテストで検知する方法

Claudeは現在、生成するファイルに署名付きC2PAプロベナンスメタデータを付与しています。OpenAIの画像モデルやGeminiも同様です。プロベナンス信号はすでにアップロードエンドポイントに届いていますが、利用者が確認する前に画像パイプラインが削除している可能性があります。

Apidogを今すぐ試す

これは悪意によるものではなく、多くのツールのデフォルト動作です。たとえば sharp().resize() は、明示的に指定しない限りメタデータを含まない新しいファイルを生成します。ImageMagick、Pillow、多くの画像CDNも同様です。マニフェスト付きの入力を受け取り、最適化済みJPEGを返しても、ログには何も残りません。

この失敗は簡単にテストできます。この記事では、通常の画像パイプラインでC2PAメタデータが失われる箇所を特定し、実際の配信経路で検証し、CIで再発を防止する方法を説明します。ApidogでAPIラウンドトリップをオーケストレーションし、c2patool でバイトレベルの検証を行います。

実際に破壊されるもの

C2PAマニフェストは、ファイルコンテナに埋め込まれる暗号署名付きのデータブロックです。署名者とアセットに対する主張を記録します。署名済みのため、再署名なしにファイルのバイトを変更すれば、検証ツールは署名不一致を検出できます。

重要なのは、C2PAがコンテナレベルのデータだという点です。画像コンテナを再作成すると、マニフェストは失われるか、残っていても署名が無効になります。

操作 マニフェストはデフォルトで残るか?
バイト単位のコピーまたは移動 はい
sharp().resize().toBuffer() いいえ
ImageMagick convert / magick いいえ
Pillow Image.save() いいえ
PNG → WebP、JPEG → AVIF いいえ
画像CDNの自動最適化 通常はいいえ
スクリーンショット いいえ
画像エディタからの再保存 いいえ
変換なしのS3アップロード はい

「いいえ」に該当する処理は、一般的なWebアプリケーションが日常的に行う処理です。

  • サムネイル生成
  • レスポンシブ画像の生成
  • フォーマットネゴシエーション
  • EXIFの削除
  • CDNでの圧縮・最適化

各処理は合理的でも、プロベナンスチェーンを静かに終了させることがあります。

特に注意が必要なのは、プライバシー対策としての -strip です。EXIFにはGPS座標やカメラのシリアル番号が含まれることがあるため、削除自体は妥当です。しかし、すべてのメタデータを一括削除するとC2PAマニフェストも消えます。プライバシーとプロベナンスを両立するには、必要なEXIFブロックだけを選択的に削除してください。

2分で問題を証明する

まず、実際に問題が起きているか確認します。有効なC2PAマニフェストを持つ画像を1つ用意してください。Claudeで生成した画像、またはContent Authenticity Initiativeの署名済みサンプルを使用できます。

1. c2patool をインストールする

cargo install c2patool
Enter fullscreen mode Exit fullscreen mode

2. フィクスチャが署名済みであることを確認する

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

クレームジェネレーターや署名ステータスを含むJSONレポートが出力されるはずです。

3. 実際のアップロード・配信経路を通す

ローカルの画像処理関数ではなく、ユーザーが利用する実際のAPIエンドポイントと配信URLをテストしてください。

# 実際のエンドポイントへアップロードする
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

# フロントエンドが利用する配信URLから取得する
ASSET_URL=$(jq -r '.url' /tmp/upload.json)
curl -sS "$ASSET_URL" -o /tmp/roundtrip.png

# マニフェストが残っているか確認する
c2patool /tmp/roundtrip.png
Enter fullscreen mode Exit fullscreen mode

結果は次の3種類です。

  1. 有効なレポート

    マニフェストと署名が有効です。配信経路でプロベナンスが維持されています。

  2. マニフェストが見つからない

    パイプラインのどこかでメタデータが削除されています。最も一般的な失敗です。

  3. 検証エラー

    マニフェストは存在しますが、署名が現在のバイト列と一致していません。画像を変更した後、古いマニフェストだけが残った状態です。

特に3番目は追跡すべき状態です。変換処理がメタデータブロックをコピーした一方で、ピクセルデータを書き換えた可能性があります。

マニフェストを壊すステップを特定する

ラウンドトリップが失敗したら、パイプラインを二分法で調査します。推測せず、各ステージの直後に c2patool を実行してください。

c2patool /tmp/after-upload.png
c2patool /tmp/after-resize.png
c2patool /tmp/after-cdn.png
Enter fullscreen mode Exit fullscreen mode

典型的な原因を、確認優先度の高い順に見ていきます。

1. リサイズ・サムネイル生成

最も可能性が高い原因です。sharp は、明示しない限りメタデータを破棄します。

// C2PAマニフェストを破棄する可能性がある
await sharp(input).resize(1200).toFile(output);

// メタデータブロックを保持する
await sharp(input).resize(1200).keepMetadata().toFile(output);
Enter fullscreen mode Exit fullscreen mode

ただし、keepMetadata() だけでは十分ではありません。画像のピクセルを変更した場合、元の署名は新しい出力バイト列に対して有効ではなくなります。

有効なプロベナンスチェーンを維持するには、次の両方が必要です。

  1. C2PAマニフェストを保持する
  2. 変換後の出力を再署名し、変換をアクションアサーションとして記録する

リサイズの場合は通常、c2pa.resized などのアクションアサーションを記録します。Rust、Python、JavaScript、C向けの c2pa ライブラリで対応できます。

2. フォーマット変換

AVIFやWebPを配信する場合、新しい画像コンテナが作成されます。

JPEG → AVIF
PNG  → WebP
Enter fullscreen mode Exit fullscreen mode

この場合も選択肢は同じです。

  • マニフェストを保持し、出力を再署名する
  • プロベナンスチェーンがここで終了することを許容し、その状態を明示する

単にメタデータをコピーするだけでは、変換済みファイルに対する署名検証は成立しません。

3. CDNの自動最適化

多くの画像CDNは配信時に画像を変換します。コンテンツクレデンシャルをネイティブに保持・再署名するサービスもありますが、歴史的にはメタデータを削除する構成が一般的です。

必ずオリジンURLではなく、ユーザーが実際にアクセスする配信URLでテストしてください。

curl -sS "https://cdn.example.com/assets/image.png" -o /tmp/cdn-image.png
c2patool /tmp/cdn-image.png
Enter fullscreen mode Exit fullscreen mode

オリジンだけを検証すると、CDNで起きる変換を見逃します。

4. アップロード時の正規化

取り込み時に画像を再エンコードするサービスも確認対象です。形式を統一するための処理は、アプリケーションコードではなくインフラストラクチャ用リポジトリに置かれていることがあります。

アップロード直後、保存直後、変換後、CDN配信後の各段階で検証してください。

恒久的なテストにする

一度限りの curl テストは、今日の状態しか証明しません。次のスプリントでリサイズ処理やCDN設定が追加されても検出できるよう、CIに組み込みます。

実装は2レイヤーに分けるのが実用的です。

  1. Apidog: APIのアップロード・取得・レスポンス検証
  2. c2patool: 取得した実ファイルに対する署名検証

レイヤー1:Apidogでラウンドトリップをテストする

Apidogでは、署名済みフィクスチャをアップロードし、レスポンスの配信URLを使って取得するテストシナリオを作成します。

ステップ1:POST /v1/assets

設定内容:

  • Body: 署名済みフィクスチャを添付した multipart/form-data
  • Assertion: ステータスが 201
  • Assertion: レスポンスが期待するスキーマに一致
  • Post-response script: レスポンスのURLを環境変数へ保存

ファイルアップロードの設定は、ApidogでファイルアップロードAPIをテストする方法と同じです。

const body = pm.response.json();

pm.environment.set("ASSET_URL", body.url);

pm.test("アップロードは配信URLを返す", function () {
  pm.expect(body.url).to.be.a("string").and.to.include("https://");
});
Enter fullscreen mode Exit fullscreen mode

ステップ2:GET {{ASSET_URL}}

確認する項目:

  • ステータスが 200
  • Content-Type が想定どおり
  • レスポンスサイズがアップロード元と大きく乖離していない
const uploadedBytes = Number(pm.environment.get("FIXTURE_BYTES"));
const returnedBytes = pm.response.responseSize;

pm.test("アセットはサイレントに再エンコードされていない", function () {
  pm.expect(returnedBytes).to.be.above(uploadedBytes * 0.9);
});
Enter fullscreen mode Exit fullscreen mode

ファイルサイズは署名検証の代わりにはなりません。あくまで大きな再エンコードや圧縮を低コストで検知するヒューリスティックです。標準的なアサーションの作り方は、APIアサーションを参照してください。

レイヤー2:CIでバイトレベル検証を行う

C2PA署名の検証にはコンテナ解析が必要です。HTTPテストの責務ではなく、c2patool をCIステップとして実行する責務です。

以下はGitHub Actionsの例です。

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

on: [pull_request]

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

    steps:
      - uses: actions/checkout@v4

      - name: c2patoolをインストールする
        run: cargo install c2patool

      - name: Apidog CLIをインストールする
        run: npm install -g apidog-cli

      - name: ラウンドトリップシナリオを実行する
        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: マニフェストが残っているか検証する
        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 は重要です。これがない場合、c2patool が失敗しても後続コマンドの扱い次第でビルドが成功する可能性があります。CIが失敗を確実に検出するようにしてください。

ApidogシナリオをGitHub Actionsで実行する設定は、GitHub ActionsでAPIテストを自動化する方法も参照してください。

レイヤー3(任意):検証エンドポイントを提供する

プロベナンスを社内チェックだけでなく製品機能として扱うなら、検証結果を返す専用エンドポイントを作る方法があります。

サービス内部で c2pa ライブラリを実行し、構造化した結果を返します。

{
  "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

状態は最低でも次の3つを分けてください。

  • verified: マニフェストがあり、署名が有効
  • absent: マニフェストが存在しない
  • invalid: マニフェストはあるが、署名が無効

必要に応じて、検証ツールが利用できない状態を表す unchecked も追加します。absentinvalid を単一のブール値にまとめると、削除と改変を区別できなくなります。

このレスポンス形式はOpenAPI定義に含め、スキーマ検証の対象にしてください。リファクタリング時のフィールド削除を防げます。OpenAPI仕様を検証する方法も参考になります。

保持すべき4つのフィクスチャ

プロベナンステストには、正常系だけでなく意図的に壊した入力も必要です。

  1. 有効な署名済みファイル

    verified を期待します。過剰なメタデータ削除を検知します。

  2. マニフェストを削除したファイル

    同じ画像から exiftool -all= でメタデータを削除したものです。エラーではなく、absent を期待します。

  3. 改ざん済みファイル

    署名後にバイトを変更した署名済みファイルです。invalid を期待します。単なるブロックの存在確認ではなく、署名検証が実行されていることを確認できます。

  4. サポートされていない形式

    マニフェストをサポートしない形式です。500エラーではなく、クリーンな absent を期待します。

これらのフィクスチャはテストシナリオの近くにコミットしてください。小さく不変なフィクスチャがあることで、テストが単に通るだけでなく意味を持つようになります。

なぜCIで確認するのか

理由は3つあります。

製品上の主張を守るため

UIでプロベナンスバッジを表示していても、リサイズ後のアセットでマニフェストを削除しているなら、その表示は正しくありません。ユーザーから指摘される前にCIで検出すべき問題です。

コンプライアンス要件を検証するため

C2PAを第50条に関連する取り組みとして扱う場合、マニフェストを削除するパイプラインは機能していない制御になります。EU AI Act第50条をAPI開発者向けに解説した記事も確認してください。

プロベナンス信号を維持するため

プロベナンスはエンドツーエンドで維持されて初めて意味を持ちます。途中でマニフェストを削除すると、検証可能な信号が失われます。

Apidogをダウンロードし、まずは自分のアップロードAPIと配信URLを対象にラウンドトリップシナリオを作成してください。その後、c2patool をCIに追加して、プロベナンスが壊れた変更をマージ前に止めます。

よくある質問

画像をリサイズするとC2PAメタデータは削除されますか?

はい。一般的な画像ライブラリでは、デフォルトで削除されることが多いです。メタデータを保持するには明示的な設定が必要です。また、リサイズ後も有効な署名を維持するには、変換済みの出力を再署名する必要があります。

ファイルにC2PAメタデータがあるか確認するには?

コマンドラインで次を実行します。

c2patool <ファイル>
Enter fullscreen mode Exit fullscreen mode

または、Content Credentials検証ページにファイルをドラッグ&ドロップしてください。

リサイズ中にC2PAメタデータを保持できますか?

保持はできますが、それだけでは不十分です。マニフェストを保持したうえで、c2pa.resized などのアクションアサーションを追加し、変換後の出力を再署名してください。再署名しなければ、古い署名は新しいバイト列と一致しません。

CDNはContent Credentialsを削除しますか?

多くのCDNは自動最適化時に削除します。一部はネイティブに保持・再署名しますが、実際の挙動は配信URLを取得して検証してください。オリジンだけのテストでは不十分です。

マニフェストが「削除された」場合と「無効」な場合の違いは?

absent はマニフェストが見つからない状態です。ファイルの出所については検証できません。

invalid はマニフェストが存在する一方で、署名が現在のバイト列と一致しない状態です。署名後にファイルが変更されたことを示します。

この2つは必ず別の状態として扱ってください。

ApidogはC2PA署名を直接検証できますか?

Apidogはアップロード・取得のラウンドトリップをオーケストレーションし、検証エンドポイントが返すJSONをアサートできます。署名そのもののコンテナ解析は c2patool の役割です。CIステップまたは独自サービス内で c2patoolc2pa ライブラリを実行してください。

EXIFは削除したいが、C2PAは保持できますか?

それが正しい目標です。一括の -strip はEXIFとC2PAマニフェストの両方を削除します。削除したいEXIFブロックだけを選択し、C2PAマニフェストは維持してください。

要点

C2PAプロベナンスメタデータはAPIに無傷で届いても、画像のリサイズ、変換、CDN最適化によって失われやすいデータです。監視ログだけでは検出できません。

対策は次の3つです。

  1. 署名済みフィクスチャを用意する
  2. 実際の配信URLを通るラウンドトリップをテストする
  3. CIで c2patool を実行し、署名エラーやマニフェスト欠落時にビルドを失敗させる

短時間のセットアップで、UI上のプロベナンス表示を、実際のパイプラインで検証される保証に変えられます。

Top comments (0)