自分のPythonコードベースをLLMでセキュリティ監査してみた—わずか$0.90で判明したこと

Dev.to / 2026/4/20

💬 オピニオンDeveloper Stack & InfrastructureSignals & Early TrendsTools & Practical UsageIndustry & Market MovesModels & Research

要点

  • 著者は、PDFの有料セキュリティ監査サービス(VibeScan)を自分のPythonコードベースで試し、プロンプトキャッシュを使って約22秒で124ファイルをスキャンした結果、LLMコストは$0.90だった。
  • 監査では重大(critical)問題は見つからなかった一方で、高(high)1件と中(medium)2件のリスクが指摘され、そのうち1件は著者が同日に実際のバグとして修正した。
  • 高度な指摘としては、スケジューラが起動するサブプロセスの標準出力/標準エラーをサイズ制限なくSQLiteの台帳に書き込む設計があり、大量出力が起きるとデータベース肥大化やメモリ枯渇につながり得る点が挙げられた。
  • 中度の指摘の1つは、GmailのOAuthリフレッシュトークンが平文のJSONファイルとしてディスクに保存されており、漏えいした場合に(リフレッシュトークンは通常期限切れしにくいため)アカウント所有者として継続的に送受信できる危険があるというものだった。
  • レポートは、残る指摘も著者がこれまで意識していなかった正当なセキュリティリスクであると位置づけ、開発中の自動セキュリティ監査の実用性を強調している。

先週、VibeScan という小さなプロダクトを出荷しました。Lovable / Bolt / Cursor / Replit / v0 で作られたアプリ向けの、49ドルの PDF セキュリティ監査です。誰かに支払いをお願いする前に、私は自分のコードベースでそれを実行し、スモークテストとして確認しました。

スキャン可能な Python ファイル 124、LLM バッチ 4、合計のウォールタイム 22 秒。監査コスト:プロンプトキャッシュ付き Opus 4.7 で $0.90。結果:クリティカル 0件、高 1件、中 2件。このうち 1 件は、同じ時間に私が直した実際のバグでした。残り 2 件は、私が考慮していなかった正当なリスクの指摘です。

以下に、各指摘に関する文脈付きの全文レポートを示します。

[HIGH] サブプロセスの stdout/stderr がサイズ上限なしで台帳に書き込まれる

場所:— スケジュールされたすべてのジョブを起動する関数。

(たとえばスクレイパーが HTML を大量に出力するなど)ログをメガバイト単位で垂れ流す暴走スクリプトは、そのすべてを SQLite の台帳に押し込みます。結果としてデータベースが肥大化し、キャプチャ中にメモリ問題を引き起こす可能性があります。たった 1 回の失敗実行で数百 MB 書き込むこともあり得ます。

Fix: にて、返す前に stdout/stderr を最後の約 10KB に切り詰めます(例: )ので、過大な出力が台帳やメモリを破壊できないようにします。

なぜ重要か

私は Python の subprocess.run を使い、 を指定していました。このフラグは、子プロセスが終了するまで Python が stdout と stderr をメモリに保持することを意味します。cron ジョブが 50 行程度出力して終了するなら問題ありません。ですが、そのジョブの 1 つが、訪問したすべてのページの HTML を吐き出す Web スクレイパーだったり、100 万行のテーブルに対してレコードごとに 1 行出力するような ETL だったりすると、出力されたバイトのすべてが、SQLite の台帳に書き込まれるまで、スケジューラープロセスの RAM 上に居座ります。

私はそれまで気づいていませんでした。スケジューラーは数週間動いていましたが、これに当たっていないのは、現状のジョブがどれもきちんと振る舞っているからです。しかし次に誰かが追加するジョブが、悪い日に 500MB を吐き出すものになるかもしれません。

修正は 4 分で済みました。返す前に各ストリームを 50KB で上限設定します。もしこれをセキュリティ監査人が指摘してくれていたら私は 200 ドルを払ったでしょう。VibeScan はリポジトリ全体で $0.90 でした。

[MEDIUM] Gmail OAuth のリフレッシュトークンが平文で保存されている

場所:30 行目— Google OAuth 認証情報を読み込む関数。

Gmail のリフレッシュトークンは、ディスク上にプレーンな JSON ファイルとして保存されます。そのファイルを読める人(バックアップ、盗まれたラップトップ、サーバ侵害など)がいると、アカウント所有者としてメールを送受信するための無期限アクセス権を得ます。リフレッシュトークンは期限切れしません。

Fix:少なくとも、 credentials/ を .gitignore に入れ、ファイル権限を 0600 にします。より強固にするなら、トークンを保存時に暗号化します(たとえば OS のキーチェーンまたは暗号化した環境変数で)し、Google アカウント -> セキュリティ -> サードパーティアプリからの失効(リボケーション)手順を文書化してください。

典型的な防御の多層化(defense-in-depth)問題です。直ちに悪用されるわけではありませんが、もし漏洩したら後悔してしまう類のことです。

[MEDIUM] HTML メール本文を無頓着な regex で除去してしまう

場所:215 行目—受信メールを正規化する関数。

extract_plain_body は、受信メールから HTML タグを除去するために正規表現を使っています。しかしその結果、スクリプト/スタイルの内容、エンコードされたエンティティ、あるいは壊れたマークアップが、分類器が見るプレーンテキスト内に残る可能性があります。そのテキストが後で LLM プロンプトに投入されたりユーザーに表示されたりするなら、攻撃者が細工したメールによって、人間には HTML として見えないコンテンツを「紛れ込ませる」ことができます。

受信メールのパイプラインは、そのプレーンテキスト版を LLM 分類器(サポートメールの振り分け用)に投入します。下流が LLM なので、これは プロンプトインジェクションの攻撃面です。LLM 経由でメールをルーティングしていることを知っている、巧妙なスパマーは、スタイルタグや HTML コメントの中に隠したコンテンツを含む HTML を作り、人間の受信者には空に見えても、LLM の入力としては指示として可視化されるようにできます。

現時点では悪用されていません。しかしこの手のバグは、18 か月後に見出しになる種類のものです。修正は BeautifulSoup への差し替えで 20 行の置換で済みます。

このスキャンのコストと、見落としたもの

  • 入力トークン:4 バッチにまたがる prompt caching で 176,364
  • 出力トークン:779
  • ウォールタイム:22 秒
  • 直接のインフラコスト:$0.90

コンサルタント相当なら、124 ファイルのリポジトリで 3〜5 時間。$600〜1500 で、エンジニアが翻訳して初めて実行可能になるレポートが出てくるはずです。VibeScan のレポートは買い手が話す言語で書かれており、変更すべき正確な行まで含まれています。

スキャンが見落としたこと(正直な制限)

  1. ビジネスロジック上の欠陥(クライアントサイドの価格を信頼してしまうようなチェックアウトなど)。
  2. 並行性の問題(状態更新の競合は、静的な読み取りではなく実行時のトレースが必要)。
  3. 依存関係の脆弱性— 私たちは package.json を CVE データベースと照合していません。Snyk はそれをより良くやってくれます。
  4. 本番インフラ— デプロイ済みのインフラではなく、コードをスキャンします。

AI でコードを書くアプリを 1 人で立ち上げて運用している創業者にとって、VibeScan が拾う指摘は、実際の失敗が起きる場所です。専任のセキュリティエンジニアを抱えるエンタープライズの開発チームにとっては、Snyk に加えて手動レビューと脅威モデリングを行うのがより良い手順になります。

あなたのリポジトリで試してみてください

Lovable / Bolt / Cursor / Replit / v0 で何かを出荷したのなら、そして現実のお金を現実のユーザーから受け取ろうとしているなら、まずコードに対して第二の目を入れてください。

49 ドルの一回払い、約 10 分で PDF:systag.gumroad.com/l/vibescan

この記事の最初の 10 人の読者には無料で提供します。リポジトリ URL を DM してください。日中のうちに PDF を返送します。