0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claudeが書いたコードをGLM-5.3に攻撃者の目で査読させる手順と見つかった穴

0
Posted at

Claude(Opus)が実装し、テスト200件超が通ったヘッドレスCMSの差分を、別のAIであるGLM-5.3(Z.ai)に「攻撃者の目で読め」と5回通した。
指摘は延べ55件。Opusが実物のコードで1件ずつ確かめ、20件を採用した。1回目の査読で、他人のアカウントを乗っ取れる重大な穴が見つかっている。
翌日に自社のSEOツールでも同じ手順を回すと、GLMの指摘を受けて足した修正のほうに、新しい穴が開いていた。

前提

事例1: 自社のヘッドレスCMS

  • 構成: Cloudflare Workers・D1・R2
  • 追加した機能(1日で実装)
    • 承認フロー
    • ID/パスワードログイン
    • 公開終了日時
    • 添付ファイル
    • 地図/動画の埋め込み
  • 実装: すべてClaude(Opus)
  • 査読前の状態: テスト200件超が通過済み、ローカルの動作確認も済み
  • 査読役: GLM-5.3(Z.ai)

事例2: 自社のSEOツール

  • 追加した機能: 関連キーワードの取得、URLの取得
  • 査読の対象: 前日の差分とツール全体
  • 査読役: GLM-5.3
  • 査読の回数: 4回

手順

査読は以下の流れで回した。

1. Claude(Opus)が機能を実装し、テストとローカルの動作確認を通す
2. 差分を GLM-5.3 に渡し、「攻撃者の目で読め」と指示する
3. GLM-5.3 の指摘を Opus が実物のコードで1件ずつ確かめ、採否を決める
4. 採用した指摘だけを直す
5. 直した差分を、もう一度 GLM-5.3 に通す
6. 新しい穴が出なくなるまで 2〜5 を繰り返す

手順3で、GLMの指摘をそのまま正としては扱わない。事例1では55件中35件を捨てている。
GLMは「攻撃者の目で」読むので、実際には到達できない経路や、すでに別の場所で防いでいる入力まで指摘してくる。
採否を実物のコードで確かめる工程を飛ばすと、不要な修正が増え、修正した箇所がまた次の穴の入口になる(事例2で実際に起きた)。

事例1: 査読5回の記録

査読は2026-10-17 23:25から10-18 00:04までの間に5回回した。

回 何を通したか 指摘 採用 判定
1 機能追加の差分(本番に出す直前) 7 5 重大
2 査読の門のスクリプト自体 7 5 要確認
3 門の修正 6 0 要確認
4 その日の変更全体を4塊に分けて通し直し 28 9 重大
5 4回目の修正の差分 7 1 要確認

合計は指摘55件・採用20件・不採用35件。

表から読み取れる点は3つある。

  • 1回目で「重大」が出た。 テスト200件超が通り、Claudeも問題を挙げていなかった差分だった
  • 4回目で再び「重大」が出た。 差分単位ではなく、その日の変更全体を4つの塊に分けて通し直すと、指摘が28件に増えた。差分だけ見ていると、変更どうしの組み合わせで生まれる穴を見落とす
  • 5回目でも新しい穴が1件出た。 直した差分を通し直す工程は、1回では終わらない

2回目と3回目は、査読を回すためのスクリプト(門)そのものを査読にかけた回になる。査読の仕組み自体にも穴があり得るので、仕組みも同じ目で読ませた。

事例1で見つかった重大な穴: 他人のアカウントにパスワードを付けて乗っ取れた

1回目の査読でGLMが見つけた穴。

  • 場面: サイトのオーナーが、メンバーにIDとパスワードを発行できる機能
  • 穴: メンバーを追加するときに、メールの確認が無い。そのため、他人のメールアドレスでメンバーを作り、パスワードを付けることで、そのアカウントを乗っ取れた

「オーナーがメンバーを招待する」という正常系だけを見ると、メールアドレスを入力してパスワードを発行する流れは自然に見える。
テストも正常系の動きを確かめるので、全部通る。
「入力したメールアドレスが、本当にその人のものか」を確かめる工程が無いことは、悪用する側の視点に立たないと気づきにくい。

自治体向けの提案に合わせて1日で5つの機能を足した日だった。ID/パスワードログインを足した日に、ログインまわりで重大な穴が出ている。

事例2: 直した修正のほうに穴が開いた

翌日(2026-10-18)、自社のSEOツールに関連キーワードとURL取得の機能を足し、前日の差分とツール全体をGLM-5.3で4回査読した。
指摘27件をOpusが実物で確かめ、10件を直して本番に出した。

ここで起きたのは、GLMに言われて足した修正のほうに穴が開くという現象だった。

落とし穴1: 回数の上限が同時リクエストで素通りできた

GLMの指摘を受けて、機能の呼び出しに回数の上限を足した。
その上限が、同時にリクエストを送ると素通りできた。

「今の回数を読む → 上限未満なら処理する → 回数を増やす」と工程を分けて書くと、読んでから増やすまでの間に別のリクエストが割り込める。
同時に送られたリクエストは、どれも「まだ上限未満」と読んでしまう。

落とし穴2: 「localhostなら通す」がHostヘッダーで破れた

開発中にログインを省くための「localhostなら通す」という修正が、Hostヘッダーの偽装で破れた。
Hostヘッダーはリクエストを送る側が自由に書ける値なので、本番に対して localhost を名乗るリクエストを送れば、開発用の抜け道に入れてしまう。

対処

回数の上限

上限は1文のSQLで先に予約する形に変えた。
回数を読む処理と増やす処理を1文にまとめ、上限に達していたら予約自体が失敗するようにした。読んでから増やすまでの隙間が無くなるので、同時リクエストでも上限を超えない。

開発用ログイン

開発用ログインは、設定ファイルの明示フラグだけで許す形に変えた。
リクエストに含まれる値(Hostヘッダー)で開発環境かどうかを判定するのをやめ、サーバー側の設定で明示したときだけ開発用ログインが有効になる。

本番でHostを偽装したリクエストを送り、403が返ることを確かめてから出した。

2つの事例から言えること

項目 事例1(ヘッドレスCMS) 事例2(SEOツール)
査読の回数 5回 4回
指摘 延べ55件 27件
採用・修正 20件 10件
主な穴 メール確認なしでの乗っ取り 修正で足した上限・開発用ログインの穴
  • テストが通ることと、攻撃に耐えることは別物だった。事例1は、テスト200件超が通った状態で重大な穴が見つかった
  • 別のAIの指摘は、そのまま正にしない。事例1では55件中35件を捨てた。採否は、書いた側のAIが実物のコードで確かめて決める
  • 直した差分は、もう一度通す。事例1では5回目でも新しい穴が出て、事例2では修正そのものが穴になった
  • 差分だけでなく、その日の変更全体を塊に分けて通し直す。事例1では、その工程で指摘が28件に増え、重大な穴も見つかった
  • 開発環境かどうかの判定に、リクエスト側が書ける値(Hostヘッダー)を使わない。サーバー側の設定で明示する
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?