
テストが決して捕捉するようには書かれていなかった脆弱性を見つけ出しましょう。CVE-2023-5968 をモデル化した ReGrade のデモ: アプリケーションをそれ自身と比較することで、パスワードハッシュの漏洩を検出します。
ReGrade によるゼロデイ発見のハンズオンデモです。 通常のテストスイートを ReGrade に向け、小さなサービスに対して記録し、同じサービスの2つ目のコピーに対してリプレイし、Claude Code + ReGrade MCP ツールを使ってパスワードハッシュ漏洩を表面化させます — どのテストも発見するために書かれていなかった脆弱性です。
これは実際のバグをモデルにしています: CVE-2023-5968。コラボレーションプラットフォームのユーザー名更新エンドポイントが、bcrypt パスワードハッシュを含むユーザーオブジェクト全体を返していました。2017年にリリースされ、7年間、テスト・レビュー・監査をすり抜けてきました。(Curtail の解説記事)
ポイント: v2 はありません。アプリをそれ自身と比較します。新しいインスタンスは異なる bcrypt ソルトでパスワードをハッシュ化するため、漏洩したハッシュの値は2つのコピー間で異なります — そして、そのエントロピーを ReGrade が検出します。この脆弱性は、あなたがすでにリリースしたコードの中に潜んでいます。それを見つけるのにバージョン変更は必要ありません。
regrade センサーをインストールして、REGRADE_API_KEY(または ~/.regrade/key)を設定します。claude plugin marketplace add https://app.regrade.curtail.com/downloads/latest/marketplace.json
次に claude plugin install regrade@regrade --scope user を実行し、一度接続します(/mcp で、キーと同じアカウントにサインイン)。ユーザーとチャンネルを持つ小さな「チームチャット」API(app/store.py)。Docker Compose は2つの同一コピーを実行します — 同じイメージ、同じコード、バージョンフラグなし:
http://localhost:8001)— ここに対して記録します。http://localhost:8002)— ここに対してリプレイします。1つのエンドポイントに仕込まれた欠陥があります:
| エンドポイント | 動作 |
|---|---|
GET /users/<id> | サニタイズ済み — パスワードを返すことはありません。クリーンなベースライン。 |
| (名前変更) |
パスワードは起動時に bcrypt でハッシュ化されるため、instance-a と instance-b は同じユーザーに対して異なるハッシュを保持します。
git clone https://github.com/Curtail-Inc/hello-ReGrade-security
cd hello-ReGrade-security
docker compose up -d --build
traffic/test_api.py は通常の機能テストスイートです: ログイン、ユーザーの読み取り、ユーザーの名前変更、チャンネルの一覧表示。CRUD の動作を検証し、セキュリティに関する検証は一切行いません — password が漏洩するかどうかは決してチェックしません。(なぜチェックするのでしょうか? そのバグの存在を誰も知らなかったのです。)
instance-a の前にセンサープロキシを起動します:
regrade proxy --target http://localhost:8001 --port 19870
別のターミナルで、同じテストスイートを実行します — BASE_URL をプロキシに向けるだけです。これが唯一の変更です:
BASE_URL=http://localhost:19870 python -m pytest traffic/test_api.py
すべてのテストは変更なしでパスします。プロキシを停止(Ctrl-C)すると、記録がアップロードされ、Recording ID: <uuid> が表示されます — それを控えておいてください。
regrade replay --rec-id <RECORDING_ID> --target http://localhost:8002
両側で同じコード — 唯一の違いは、新しいインスタンスが生成する値だけです。
このリポジトリを Claude Code で開き、リプレイを案内してもらいます。このリポジトリの CLAUDE.md に導かれ、次のことを行います:
summarize_deltas を実行 → いくつかの差分: ログインの token、チャンネルの created_at タイムスタンプ、そして — その中に静かに混ざって — $.password。$.token に create_id_mapping(動的 ID であり、破棄すべきノイズではない)、$.channels[*].created_at(起動ごとのタイムスタンプ)に create_filter_rule で DROP、apply_profile_to_replay を実行し、次に query_deltas(unlabeled_only=true) を実行 — 残る差分が1つだけになるまで
繰り返します。$.password — レスポンスボディ内の bcrypt ハッシュです。事前知識ゼロ、セキュリティ検証ゼロで発見された CVE 級の漏洩です。セッショントークンとパスワードハッシュは、どちらも2回の実行間で異なります — どちらも毎回変化する高エントロピー文字列です。一方は マッピング する正当なノイズであり、もう一方は**侵害(リーク)**です。「変化した」という事実だけでは両者を区別できません — 何が変化したのかを見る必要があります。すべての高エントロピーフィールドをノイズとして除外してしまえば、脆弱性を隠してしまうことになります。
これが教訓です: ReGrade は期待値を検証するのではなく、動作を比較します — だからこそ、誰もテストを書こうと思わなかったバグを浮き彫りにできるのです。
app/store.py は1つのファイルです。GET パスは sanitize() を呼び出しますが、PATCH パスは呼び出し忘れています。ReGrade で見つけた後にコードを見てください — 重要なのは、ReGrade がアプリをそれ自身と比較し、トラフィックだけでこれを検出したことです。
Apache-2.0.
PATCH /users/<id>バグ — bcrypt の password ハッシュを含むユーザーオブジェクト全体を返します。sanitize() 呼び出しが1つ欠落しており、CVE-2023-5968 とまったく同じです。 |