Skip to content

fix(x402): 有料エンドポイントの応答を no-store にし共有キャッシュへ載せない - #277

Merged
cipherwebllc merged 1 commit into
mainfrom
fix/x402-402-no-store
Jul 27, 2026
Merged

fix(x402): 有料エンドポイントの応答を no-store にし共有キャッシュへ載せない#277
cipherwebllc merged 1 commit into
mainfrom
fix/x402-402-no-store

Conversation

@cipherwebllc

Copy link
Copy Markdown
Owner

背景

本番を実測したところ、/api/paid/* が Next.js 既定の cache-control: public, max-age=0, must-revalidate を返していました。

$ curl -sI https://open-pay.jp/api/paid/japan-web3-directory
HTTP/2 402
cache-control: public, max-age=0, must-revalidate   ← public = 共有キャッシュ保存を許可

402 は支払い条件(payTo / 金額 / forwarder / commitVersion)、200 は購入者だけが受け取るべき有料コンテンツを含みます。VaryX-PAYMENT が無いため、URL だけを鍵にしたキャッシュが別の買い手へ再配信し得る形でした。

変更

handleFirstPartyPaidGet出口で Cache-Control: no-store を一律に付与します。このハンドラは分岐が 20 以上あり、個々の return に付けると新しい分岐を足したときに漏れるため、境界 1 箇所で保証する形にしました。402 / 200 の双方で no-store を固定するテストも追加しています。

契機と、あえて触っていない範囲

x402 上流の PR(402 応答へ Cache-Control: no-store を足す議論)と、主要 Facilitator 15 件の安全規則違反を報告した査読前研究(arXiv・2026-07-21)が契機です。

研究が挙げる他の 5 系統は PR #273 で対応済みのため、本 PR では触れていません。

研究の指摘 現状
settle 直前の状態再確認 実装済(署名 recover / 残高 / nonce 未使用 / 冪等)
ガス消耗(Gas Abuse) 実装済(sub-floor settle 専用の日次枠 + payer 枠)
settle 失敗を成功として返す 実装済(Settled イベント 6 フィールド完全一致)
支払い後に商品を受け取れない 実装済(再配信)
不明状態の成功扱い 実装済(settled / unused / indeterminate の三値)

なお本文(v1)とヘッダー(v2)の同一 JSON 強制は行いません。OpenPay は二重互換設計のため、経済条件の等価性で担保する方が適切です。

検証

  • typecheck / full vitest 8036 passed (392 files) / eslint
  • next build + bundle 予算 — 全ルート内
  • audit-gate / lockfile-gate — PASS

🤖 Generated with Claude Code

https://claude.ai/code/session_015CNMEwurCJNTrzvr2qypSd

本番実測 (2026-07-27) で `/api/paid/*` の応答が Next.js 既定の
`cache-control: public, max-age=0, must-revalidate` を返しており、**共有キャッシュ
(CDN / プロキシ) への保存が許可**されていた。402 は支払い条件 (payTo / 金額 /
forwarder / commitVersion) を、200 は購入者だけが受け取るべき有料コンテンツを含む。
`Vary` に X-PAYMENT が無いため、URL だけを鍵にしたキャッシュが別の買い手へ再配信し得る。

## 変更
- `handleFirstPartyPaidGet` の出口で `Cache-Control: no-store` を一律に付与。
  分岐が 20 以上あり個々の return に付けると新規分岐で漏れるため、境界 1 箇所で保証する
  (掟13: 何の波及を断つ防御かをコメントで明記)。
- 402 と 200 の双方で no-store を固定するテストを追加。

契機は x402 上流の PR (402 応答へ Cache-Control: no-store を足す議論) と、
主要 Facilitator 15 件の安全規則違反を報告した査読前研究 (arXiv 2026-07-21)。
研究が挙げる他 5 系統 (settle 前の状態再検証 / ガス消耗 / settle 失敗の成功扱い /
支払い後の未配信 / 不明状態の成功扱い) は PR #273 で対応済みのため本 PR では触れない。

## 検証
typecheck / **full vitest 8036 passed (392 files)** / next build + bundle 予算 全ルート内 /
eslint / audit-gate / lockfile-gate PASS。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CNMEwurCJNTrzvr2qypSd
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
openpay Ignored Ignored Preview Jul 27, 2026 1:05pm

@cipherwebllc
cipherwebllc merged commit d745b23 into main Jul 27, 2026
6 checks passed
@cipherwebllc
cipherwebllc deleted the fix/x402-402-no-store branch July 27, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant