◉ANTIGRAVITY LABEN
記事一覧/アプリ開発
▣ アプリ開発/2026-06-25上級

「広告を見たら壁紙を解放」をエージェントがクライアント側だけで付与していた — リワード報酬をSSVで検証し直した記録

リワード広告を見たら壁紙を解放する機能を Antigravity のエージェントに頼んだら、解放フラグをクライアント側だけで書き込む実装が返ってきました。なぜそれでは足りないのか、AdMob のサーバーサイド検証(SSV)で報酬付与を検証し直し、二重付与まで止めた設計を記録します。

antigravity464admob16monetization15app-dev52security17

✦ プレミアム記事

リワード広告を一本見たら、有料の壁紙を一枚だけ解放できる。そんな小さな導線を Antigravity のエージェントに頼んだとき、返ってきたコードは確かに動きました。広告が閉じた瞬間に壁紙が解放され、次に開いたときも解放されたままです。ただ、その「解放されたまま」を支えていたのは、端末のローカル設定に書き込まれた一個の真偽値だけでした。

私は個人開発で壁紙・ヒーリング系のアプリを Google Play で運用しています。課金のない代わりに、リワード広告の視聴で一部の壁紙を解放する設計を長く使ってきました。だからこそ、この実装を一目見て手が止まりました。これは、解放という報酬付与を誰が決めるのか、という信頼境界の話だからです。

「広告を見たら解放」を、エージェントは一番短い道で実装した

エージェントが書いたのは、おおよそ次のような形でした。広告の視聴完了コールバックで、その壁紙の解放フラグをそのまま端末に書き込みます。

// エージェントが最初に提案した版(クライアント側だけで付与)
rewardedAd.show(activity) { rewardItem ->
    // 視聴完了 = 即・解放を端末に永続化
    prefs.edit()
        .putBoolean("wallpaper_${wallpaperId}_unlocked", true)
        .apply()
    unlockUi(wallpaperId)
}

挙動としては正しく見えます。広告を見れば解放され、再起動しても残ります。テストでも問題は出ません。けれども、この一行 putBoolean(... true) が「壁紙が解放されたか」の唯一の根拠になっている点が、本番では弱点になります。

端末のローカル設定は、ユーザーの手の届く場所にあります。root 化された端末や改変したビルドでは、広告を一度も見ずにこのフラグを true にできます。広告のコールバック自体も、クライアントの中で完結している限りは差し替えの対象です。つまり「広告を見た」という事実と「解放してよい」という判断が、どちらもクライアント側に閉じていました。

少額とはいえ、これは広告収益の取りこぼしに直結します。リワード広告は「視聴という対価と引き換えに報酬を渡す」契約で成り立っているのに、対価を払わずに報酬だけ取れる経路を残してしまうからです。

なぜクライアント側の付与だけでは足りないのか

ここで効いてくるのが、AdMob のサーバーサイド検証(Server-Side Verification, SSV)です。リワード広告ユニットで SSV を有効にすると、ユーザーが報酬を得るたびに Google が私のサーバーへコールバックURLを叩いてくれます。このコールバックには、報酬を「付与してよい」とGoogleが認めた事実が、ECDSA の署名つきで載っています。

公式ドキュメントでも、クライアント側のコールバックは即時のUX用に使い、報酬の正当性はSSVで検証するのが推奨とされています(Validate server-side verification (SSV) callbacks)。アプリ内経済に影響する報酬ほど、サーバーの検証済みコールバックを正とすべきだと明記されています。私の壁紙解放はまさに「経済に影響する報酬」でした。

設計として持ち帰るべき線は一本です。「広告を見た」という計測はクライアント(と広告SDK)に任せてよい。しかし「解放してよい」という付与の判断は、署名を検証できるサーバーだけが下す。 エージェントが短絡したのは、この二つを一つにまとめてしまった点でした。

観点クライアント側だけの付与SSV で検証した付与
解放の根拠端末のローカルフラグGoogleが署名したコールバック
詐称耐性フラグ書き換え・APK改変で突破可能署名検証を通らない付与は弾ける
二重付与検知できないtransaction_id で冪等化できる
真実の所在端末サーバー
✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦クライアント側の解放フラグがなぜ詐称できるのかを、AdMob のリワードSSVという一点で今日から塞げる
✦SSVコールバックを Cloudflare Worker で検証する完成コードを、DER署名をWeb Cryptoが受け取れない罠の回避ごと自分のアプリに移植できる
✦transaction_id で二重付与とリプレイを止める冪等な付与の組み方と、計測はエージェント・信頼境界は人という線引きを持ち帰れる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

▣ アプリ開発2026-03-27
AdMob のウォーターフォールを残したまま、ビッディングを足す判断
運用中の iOS 4 アプリでウォーターフォールを残したままビッディングを加えた判断を、ATT と初期化の順序、広告ゲートの設計、見るべき指標とともに整理しました。
▣ アプリ開発2026-09-10
iOS 27 の配信日が決まってから、私が最初に grep したのは AGENTS.md でした
iOS 27 と iPadOS 27 の配信は9月14日です。版数の文字列はソースだけでなく、エージェントが読む設定ファイルにも残ります。両方を数えるスクリプトと、keyword 検索では拾えない一行の話を書き残します。
▣ アプリ開発2026-09-04
iOS 27 の RC から正式リリースまでの5日間 — 画面より先に、初期化の順番と権利の復元を確かめます
OS が入れ替わる直前の短い窓で、エージェントに渡す検証範囲を画面まわりではなく起動経路で切るまでの記録です。ATT と広告 SDK の順序、権利の復元、寸法のハードコード分岐を機械で洗い出す監査スクリプトを添えています。