Claudeは現在、生成するファイルに署名付きのC2PAプロベナンスメタデータを付与しています。OpenAIの画像モデルも同様で、Geminiも同様です。これは、実際のプロベナンス信号が初めてアップロードエンドポイントに届いていることを意味しますが、誰かがそれを見る前に、あなたのパイプラインがそれを削除している可能性が高いです。
悪意からではなく、デフォルトでそうなっています。sharp().resize()は、特に指定しない限り、メタデータを含まないクリーンなファイルを生成します。ImageMagickも同様です。Pillowも同様です。ほとんどの画像CDNも同様です。マニフェストが入力され、より小さなJPEGが出力されますが、ログには何も記載されません。
これはテスト可能な失敗であり、テストは複雑ではありません。ここでは、通常のパイプラインでメタデータがどのように失われるか、それが起こっていることをどう証明するか、そしてCIにラウンドトリップチェックを組み込んでそれが再発しないようにする方法を説明します。Apidogがオーケストレーションを処理し、c2patoolがバイトレベルの検証を処理します。
実際に破壊されるもの
C2PAマニフェストは、ファイルコンテナに埋め込まれた暗号的に署名されたブロックです。誰がアセットに署名し、それについて何が主張されたかを記録し、署名されているため、再署名せずにバイトを変更すると、どのリーダーでも検出できる方法で署名が壊れます。
「コンテナレベル」が肝心なフレーズです。コンテナを書き換えると、マニフェストは消滅します。
| 操作 | マニフェストはデフォルトで残るか? |
|---|---|
| バイトごとのコピーまたは移動 | はい |
sharp().resize().toBuffer() |
いいえ |
ImageMagick convert / magick |
いいえ |
Pillow Image.save() |
いいえ |
| PNGからWebP、JPEGからAVIF | いいえ |
| 画像CDNの自動最適化 | 通常はいいえ |
| スクリーンショット | いいえ |
| 画像エディタからの再保存 | いいえ |
| 変換なしのS3アップロード | はい |
「いいえ」の列にある項目はすべて、通常のWebアプリケーションが受け入れるすべての画像に対して行うことです。サムネイル、レスポンシブバリアント、フォーマットネゴシエーション、プライバシーのためのEXIFスクラブ。それぞれは単独では合理的ですが、それぞれがサイレントにプロベナンスチェーンを終了させます。
注意すべき点:プライバシーを目的とした-stripの習慣は、EXIFがGPS座標やカメラのシリアル番号を運ぶため、意図的なことが多いです。位置情報データを削除するためにすべてのメタデータを削除すると、プロベナンスマニフェストも削除されます。これら2つの目標は現在衝突しており、これを解決するには、ブロック全体を削除するのではなく、選択的である必要があります。
2分で証明する
何かを構築する前に、問題があることを確認してください。有効なマニフェストを持つファイルが1つ必要です。Claudeが生成するどの画像でも機能しますし、Content Authenticity Initiativeから署名済みのサンプルを取得することもできます。
参照CLIをインストールします。
cargo install c2patool
フィクスチャが実際に署名されているか確認します。
c2patool fixtures/signed-sample.png
クレームジェネレーターと署名ステータスを示すJSONレポートが表示されるはずです。次に、それを独自のスタックに通し、もう一方の端を確認します。
# 実際のEndpointを介してアップロードする
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
3つの可能な結果があり、それぞれが異なる意味を持ちます。
- 有効なレポート。マニフェストは残存していました。良いことです。
- マニフェストが見つからない。パイプラインのどこかで削除されました。これが一般的なケースです。
- 検証エラー。マニフェストは存在しますが、その署名がバイトと一致しません。誰かがファイルを変更し、古いマニフェストをそのまま残しました。これは削除するよりも悪い状態です。なぜなら、下流の検証者には改ざんのように見えるからです。
3番目の結果こそが追跡すべきものです。これは通常、変換ライブラリがピクセルを書き換えながらメタデータブロックを保持したことを意味します。
それを引き起こすステップを見つける
ラウンドトリップが失敗した場合、パイプラインを二分法で調査します。推測するのではなく、各ステージの直後にマニフェストを確認してください。
典型的な容疑者(可能性が高い順):
1. リサイズまたはサムネイル作成のステップ。最も可能性の高い犯人です。sharpでは、明示的に保持しない限りメタデータは破棄されます。
// C2PAマニフェストを破棄する
await sharp(input).resize(1200).toFile(output);
// メタデータブロックを保持する
await sharp(input).resize(1200).keepMetadata().toFile(output);
ブロックを保持することは必要ですが、それだけでは十分ではありません。ピクセルが変更されたため、元の署名は新しいバイトに対してはもはや検証されません。機能するプロベナンスチェーンを維持するには、出力を再署名し、変換をアクションアサーション(通常はc2pa.resized)として記録する必要があります。Rust、Python、JavaScript、Cのc2paライブラリはすべてこれをサポートしています。
2. フォーマット変換。AVIFまたはWebPを配信するということは、新しいコンテナを意味します。同じルールが適用されます:保持して再署名するか、そこでチェーンが終了することを容認してその旨を伝えるかです。
3. CDN。多くの画像CDNは配信時に書き換えを行います。一部は現在、コンテンツクレデンシャルをネイティブに保持して再署名しますが、ほとんどは歴史的にそれらを削除してきました。ユーザーが実際にアクセスする配信URLを介してテストし、オリジンを介してテストしないでください。そうしないと、意味のない「合格」結果が得られます。
4. アップロード時の正規化。取り込み時にフォーマットを標準化するために再エンコードするサービスは忘れられがちです。なぜなら、そのコードは誰も読まないインフラストラクチャのリポジトリに存在するからです。
恒久的なテストにする
一度限りのcurlは今日の状態を証明するものです。それは次のスプリントで誰かがリサイズステップを追加するのを防ぐものではありません。チェックはCIに組み込まれる必要があります。
2つの異なるツールが2つの異なるタスクに優れているため、2つのレイヤーに分割します。
レイヤー1:Apidogでのラウンドトリップ
オーケストレーションは、通常の連鎖したAPIテストです。フィクスチャをアップロードし、返されたURLをキャプチャし、実際の配信パスを介してアセットをフェッチし、返されるものについてアサートします。
Apidogでは、これは2つのステップを持つテストシナリオです。
ステップ1:POST /v1/assets
- Body:署名済みフィクスチャを添付した
multipart/form-data。そのメカニズムはファイルアップロードAPIのテストと同じです。 - Assertion:ステータスが
201であり、レスポンスがスキーマと一致すること。 - 次のステップにURLを渡すためのポストレスポンススクリプト:
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://");
});
ステップ2:GET {{ASSET_URL}}
- Assertion:ステータスが
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);
});
サイズはヒューリスティックであり、証明ではありません。これは大きな失敗を安価に検出し、他のすべてと同じスイートで実行されます。標準的なアサーションパターンはAPIアサーションでカバーされています。
レイヤー2:CIでのバイトレベルチェック
署名の検証はコンテナの解析を意味し、それはHTTPクライアントの仕事ではなくc2patoolの仕事です。ラウンドトリップでフェッチしたファイルに対して、パイプラインのステップとして実行します。
# .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
set -euo pipefailが重要です。これがないと、c2patoolが削除されたファイルで失敗した場合でも警告とグリーンビルドが生成され、まさに防ぎたかった失敗を引き起こしてしまいます。パイプラインでApidogシナリオを実行するのが初めての場合は、GitHub ActionsでのAPIテストの自動化でセットアップがカバーされています。
レイヤー3(オプション):検証エンドポイント
プロベナンスが社内チェックではなく製品機能である場合、最もクリーンな設計は、独自のサービスにc2paライブラリを実行し、構造化された結果を返す小さなエンドポイントを設けることです。そうすれば、全体が通常のJSONとしてテスト可能になり、フロントエンドは推測ではなく実際の結果を得ることができます。
{
"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"
}
}
2つではなく3つの状態を維持してください。verified、absent、invalidは genuinely 異なる意味を持ちます。absentとinvalidを1つのブーリアンにまとめると、最も興味深いシグナルを失うことになります。検証者が利用できない場合に備えてuncheckedを追加すれば、障害がクリーンな結果として偽装されることを防げます。
その形式をOpenAPI定義で文書化し、それに対して検証することで、リファクタリング中にフィールドが消えることを防ぎます。OpenAPI仕様を検証する方法では、その側面について説明しています。
保持すべき4つのフィクスチャ
プロベナンススイートは、ハッピーパスだけでなく、意図的に壊れた入力も必要とします。
- 有効な署名済みファイル。
verifiedを期待します。熱心すぎる削除を検出します。 - 削除済みファイル。同じ画像から
exiftool -all=でマニフェストを削除したもの。エラーではなく、絶対にverifiedではなく、absentを期待します。 - 改ざんされたファイル。署名後にバイトが変更された署名済みファイル。
invalidを期待します。これは、単にブロックが存在するかどうかだけでなく、署名をチェックしていることを証明するものです。 - サポートされていない形式。マニフェストサポートがまったくないもの。500エラーではなく、クリーンな
absentを期待します。
これら4つすべてをテストシナリオの隣のリポジトリにコミットしてください。それらは小さく、決して変更されず、テストが「パスする」ことと「意味を持つ」ことの違いを生み出します。
なぜわざわざ?
3つの理由があり、コストがかかる順に並べます。
製品の主張。UIにプロベナンスバッジが表示されていても、パイプラインがマニフェストを削除している場合、リサイズを経たすべてのアセットでバッジが間違っています。それはユーザーから知ることになる信頼問題です。
コンプライアンスの物語。C2PAを第50条関連の何かに依存している場合、削除されたマニフェストは機能していない制御となります。EU AI ActのAPI開発者向け第50条では、実際にあなたに課される義務について説明されています。
信号自体。プロベナンスは、チェーンがエンドツーエンドで保持されて初めて機能します。マニフェストを静かに削除するすべてのパイプラインは、エコシステム全体の有用性を低下させ、それは何かを検証しようとしているあなたにとっても同様です。
Apidogをダウンロードして、独自のエンドポイントに対するラウンドトリップシナリオを構築し、その後にc2patoolステップを組み込んでください。
よくある質問
画像のリサイズはC2PAメタデータを削除しますか? はい、一般的なすべてのライブラリでデフォルトで削除されます。メタデータブロックを保持するには明示的なフラグが必要で、有効な署名を維持するには変換された出力を再署名する必要があります。
ファイルにC2PAメタデータがあるかどうかを確認するにはどうすればよいですか? コマンドラインからc2patool <ファイル>を実行するか、ファイルをContent Credentials検証ページにドラッグアンドドロップしてください。
リサイズ中にC2PAメタデータを保持できますか? はい、しかし保持するだけでは不十分です。ブロックを保持し、次にc2paライブラリのいずれかを使用してc2pa.resizedなどのアクションアサーションで出力を再署名します。そうしないと、古い署名が新しいバイトと一致しなくなります。
CDNはContent Credentialsを削除しますか? 多くのCDNは自動最適化時に削除します。一部は現在、ネイティブに保持して再署名します。ユーザーがアクセスする配信URLを介してテストし、オリジンを介してテストしないでください。
マニフェストが削除された場合と無効な場合の違いは何ですか? 削除されたとは、マニフェストが見つからなかったことを意味し、ファイルの出所については何もわかりません。無効とは、マニフェストが存在するがその署名がバイトと一致しないことを意味し、署名後にファイルが変更されたことを意味します。これらを別々の状態として維持してください。
ApidogはC2PA署名を直接検証できますか? ラウンドトリップをオーケストレートし、検証エンドポイントのJSONを含むHTTPレスポンスをアサートします。署名解析自体はc2patoolの仕事であり、CIステップとして、または独自のサービス内で実行されます。両方を組み合わせて使用してください。
プライバシーのためにEXIFを削除するが、C2PAは保持すべきですか? それが正しい目標であり、選択的なアプローチが必要です。一括-stripは両方を削除します。特に削除したいEXIFブロックを削除し、C2PAマニフェストはそのままにしておきます。
要点
プロベナンスメタデータはAPIに無傷で到着しますが、通常はバラバラになってしまいます。そして、モニタリングでは何も教えてくれません。解決策は、フィクスチャ、実際の配信パスを経由するラウンドトリップ、そしてビルドを失敗させるc2patoolチェックです。
20分間のセットアップで、UIで主張していることを、パイプラインが実際に強制する保証に変えることができます。
