AIエージェントによるセキュリティレビューことはじめ

はじめに

プレイドでは社内での脆弱性診断も実施しており、以下のフローをセキュリティレビューと呼んでいます。

1. 依頼
2. ヒアリング
3. 脆弱性のチェック
4. フィードバック
5. 修正/レビュー
6. 再チェック

社内での脆弱性診断と外部に依頼する脆弱性診断の違いについては、過去記事をご覧ください。

生成AIによって開発効率が上がっていく中で、セキュリティレビューのプロセスも効率化し、開発速度に対応していく必要性を感じていました。

そこで、社内セキュリティレビュープロセスの一部に生成AIを活用し、セキュリティレビューを自動化・効率化する試みを開始しました。この記事では、GitHub Copilotのエージェント機能を活用したその試みと、現時点での所感について紹介します。

将来的には、セキュリティチームだけでなく、開発チーム自身が開発の早い段階でチェックを行えるようになることも目指しています。

戦略

今回は、GitHub Copilot の VS Code 拡張機能のエージェントモードを使います。

全体の流れを以下のように定め、各フェーズでの生成物は memory-bank ディレクトリに保存されるように指示します。

これにより、AIエージェントが各ステップの結果を参照しながら、段階的に分析を進めることを意図しています。

  1. ディレクトリ構造分析
  2. APIエンドポイントの分析
  3. レビュー
    1. API指定によるレビュー
    2. ディレクトリパス指定によるレビュー
  4. レポート生成

sec_review_flow.png

手順・プロンプト例

ここでは、実際の手順とプロンプトの例を紹介します。プロンプトは運用しながら改善を続けていく予定です。

前提

  • GitHub Copilot in VS Code の エージェントモード
  • Model: Claude 3.7 Sonnet

を使用しています。

1. セキュリティレビュー用のルール作成

まず、セキュリティレビューを行うためのルール (security-review.md)を作成します。

以下に例を示します。あえてレビュー観点は抽象的に指示しています。

# security review rule

## **分析プロセス**

あなたはセキュリティコード分析の専門家です。与えられたコードベースをセキュリティの観点から徹底的に調査し、潜在的な脆弱性や問題点を特定する任務を担っています。以下のガイドライン、フロー、レビュー観点に従って分析を行ってください。
省略はせず漏れの無いように分析してください。それが難しい場合は教えてください。
分析が終わったら、レポート形式に従って、memory-bank 内に新しいファイルとして結果をまとめてください。

### ガイドライン

1. **重要な前提条件**
    - XXX
2. **禁止事項**
    - memory bank以外のファイル・コードの修正は行わないこと
3. **コード構造の理解**    - コードベースの全体像、アーキテクチャ、主要なコンポーネントを把握する
    - 使用されている言語、フレームワーク、ライブラリを特定する
4. **コードレベルでの指摘**    - 特定のコードを指定した指摘が可能なものを中心にレビュー・指摘する
    - 抽象的で断定ができない指摘は排除する
5. **レビュー観点**
    - OWASP Top10やOWASP Application Security Verification Standard、CWE/SANS Top 25 などに基づいてコードをレビューしてください
    
### フロー

Security Reviewは以下のフローで行います:

flowchart TD
    A[レビュー依頼と前提条件の確認] --> B[コード分析と構造理解]
    B --> C[ガイドライン/レビュー観点に基づく脆弱性調査]
    C --> D[発見事項の整理と評価]
    D --> E[レポート作成]
    E --> F[レポート提出/保存]

## **レポート形式**

分析結果は以下の形式で、詳細に報告してください:

1. **要約**    - コードベースの全体的なセキュリティ状態
    - 発見された主要な問題点
    - 全体的なリスク評価
2. **詳細な発見事項**    - 問題:発見された脆弱性の説明
    - 場所:問題が見つかったファイル/行番号
    - 重大度:Critical/High/Medium/Low
    - 潜在的な影響:想定される詳細な攻撃シナリオと被害
    - 問題の確認方法: 問題が本当に存在するか確認するための方法
    - 修正案:問題を解決するための具体的な推奨事項
3. **推奨されるベストプラクティス**    - 全般的なセキュリティ改善策
    - セキュリティテストの推奨事項

作成したレポートは、新しいファイル `<api-name>-security-review.md` として `memory-bank` に保存してください。

2. ディレクトリ分析を行う

下記プロンプトを使用して、ディレクトリ構造の分析を行います。これによって以後のタスクの精度を上げることを目的としています。

# コードベースディレクトリ構造の詳細解析

## 目的
このプロジェクトの全体的なアーキテクチャとファイル構成を把握するため、コードベースのディレクトリ構造を詳細に解析してください。
解析後に新しいファイルとして、memory-bank に保存してください。 

## 主要タスク
1. **ディレクトリ構造の理解**
   - 全ディレクトリとサブディレクトリの階層構造を特定
   - 各ディレクトリの役割と責任の分析
   - 設計パターンやアーキテクチャスタイルの識別

2. **ファイル分析**
   - 各ディレクトリ内の主要ファイルタイプの特定と分類
   - 特に重要なファイル(設定ファイル、メインエントリーポイント等)の指摘
   - ファイル命名規則とその意味の解析

3. **相互依存関係の特定**
   - ディレクトリ間の依存関係とデータフローのマッピング
   - 循環依存性や密結合の可能性がある領域の特定
   - コア機能・モジュールの識別

4. **アーキテクチャパターンの評価**
   - 使用されている設計パターンとアーキテクチャスタイルの特定
   - レイヤード構造、マイクロサービス、モノリス等のアーキテクチャ特性の評価
   - プロジェクト特有のアーキテクチャ決定事項の識別

## 出力フォーマット
以下の構造で分析結果を提供してください:

1. **概要**
   - 全体的なディレクトリ構造の要約
   - プロジェクト規模と複雑性の評価
   - 主要なアーキテクチャパターンとスタイルの特定

2. **ディレクトリ構造の詳細分析**
   - 各主要ディレクトリの目的と内容の説明
   - ディレクトリ間の関係性と依存関係の視覚化(階層図)
   - 特筆すべき設計選択や特殊なファイル構成の指摘

3. **主要コンポーネントとモジュール**
   - コアロジックとビジネスドメインを含むディレクトリの特定
   - ユーティリティコード、共有ライブラリ、サードパーティ統合の場所
   - テスト、ドキュメント、ビルドスクリプトの構成

4. **視覚的表現**
   - ディレクトリ構造の階層図
   - 主要なコンポーネント間の依存関係図
   - モジュール間のデータフロー図

## 指示
- 最初に大まかな構造の概要を示し、その後で重要な部分を詳細に分析してください
- ライブラリやフレームワーク固有のパターンやディレクトリ構造の慣例を特定してください
- `README`ファイル、ドキュメント、コメントなどの追加コンテキストも参照してください
- 規模やコンテキストに応じて、分析の深さと範囲を調整してください

3. APIエンドポイントの分析を行う

下記のプロンプトを使用して、プロダクトのAPIエンドポイントを列挙します。

openapi.yml 等がある場合は、それを優先して読み込むように指示します。

# API 分析

各RPC APIエンドポイントを抜け漏れがないようにガイドラインに従ってテーブルにまとめてください。
method, path, parameter, body, 必要な権限, 概要
のようにAPIのカテゴリごとにまとめて、新しいファイルとして memory-bank に追加してください。

## ガイドライン

- @XXX.yml が存在する場合は、それを優先的に参照してください。
- <API定義において、補足しておく内容...>

4.1. APIレビュー

次に、 security-review.md を読み込ませて実際にレビューを依頼します。

<API> APIについて、フローに従ってセキュリティレビューを行なってください。
分析結果を memory bank に保存してください。

4.2. コードレビュー

同様に、security-review.md を読み込ませて、ディレクトリ・ファイルをコンテキストに追加しコードレビューを依頼します。

<Directory> 配下について、フローに従ってセキュリティレビューを行なってください。
分析結果を memory bank に保存してください。

(optional) 各リスクに対する深掘り

レポート時点ではやや抽象的な指摘なこともあるため、より深掘りたいリスクに対して再レビューを依頼することで精度が上がる印象です。

以上が、全体的な流れになります。

プロンプトはまだ改善の余地がありますが、これで何が検出できるのか見ていきます。

OWASP Juice Shopに対して動かしてみる

OWASP Juice Shopとは、学習用に作成された脆弱なWebアプリケーションです。

試しに、OWASP Juice Shopのユーザー情報APIを指定してみると、下記のような結果を得られました。

※ OWASP Juice Shopの場合 vuln-code-snipeet とコメントされていることもあるので、コードの特定が容易な箇所はあります

SQLi, SSTI, CSRFなどの脆弱性が検知でき、実際そこから問題を解くこともできますね。

しかし、網羅性には欠ける印象です。

# OWASP Juice Shop ユーザー情報API セキュリティレビュー

## 要約

OWASP Juice ShopのユーザーAPI(認証・ユーザー情報管理)に関するセキュリティレビューを実施しました。このレビューでは、ログイン処理、ユーザー情報取得、ユーザープロファイル管理などの機能を対象としています。

**主な発見事項**:
- SQLインジェクション脆弱性(Critical): ログイン処理で生のSQLクエリが使用されており、ユーザー入力が適切にサニタイズされていない
- サーバーサイドテンプレートインジェクション(SSTI)脆弱性(High): ユーザー名処理での`eval`使用による脆弱性
- CSRFに対する不十分な保護(Medium): アプリケーション内でCSRF保護が不完全
- 不適切なエラー処理(Low): エラーメッセージに詳細な情報が含まれている

全体的なリスク評価: **High** - 特にSQLインジェクション脆弱性は重大であり、ユーザーデータの漏洩や不正アクセスにつながる可能性が高い

## 詳細な発見事項

### 1. SQLインジェクション脆弱性

**問題**: ログイン処理において、ユーザー入力が直接SQLクエリに挿入されています。

**場所**: `/routes/login.ts` の35行目:
models.sequelize.query(`SELECT * FROM Users WHERE email = '${req.body.email || ''}' AND password = '${security.hash(req.body.password || '')}' AND deletedAt IS NULL`, { model: UserModel, plain: true })

**重大度**: Critical

**潜在的な影響**: 攻撃者は特殊な文字列を入力してSQLクエリを操作し、以下のような攻撃が可能です:
- 認証バイパス(例: `' OR 1=1--`などを入力して全ユーザーアクセス)
- データベース構造の探索
- 管理者アカウントへの不正アクセス
- データベース内の機密情報の漏洩

**問題の確認方法**: 以下のような入力でログインを試みることで検証できます:
1. メールフィールドに: `' OR 1=1--`
2. パスワードフィールドに: 任意の値

**修正案**: 
- パラメータ化クエリを使用する:
models.sequelize.query("SELECT * FROM Users WHERE email = ? AND password = ? AND deletedAt IS NULL", 
  { 
    replacements: [req.body.email || '', security.hash(req.body.password || '')],
    model: UserModel, 
    plain: true 
  })

- または、Sequelizeのクエリビルダーを使用する:

UserModel.findOne({
  where: {
    email: req.body.email || '',
    password: security.hash(req.body.password || ''),
    deletedAt: null
  }
})


### 2. サーバーサイドテンプレートインジェクション(SSTI)脆弱性

**問題**: ユーザープロファイル処理において、ユーザー名に含まれるコードが`eval()`を使用して実行されています。

**場所**: `/routes/userProfile.ts` の45-52行目:

if (username?.match(/#{(.*)}/) !== null && utils.isChallengeEnabled(challenges.usernameXssChallenge)) {
  req.app.locals.abused_ssti_bug = true
  const code = username?.substring(2, username.length - 1)
  try {
    if (!code) {
      throw new Error('Username is null')
    }
    username = eval(code) // eslint-disable-line no-eval
  } catch (err) {
    username = '\\' + username
  }
}


**重大度**: High

**潜在的な影響**: 
- 任意のJavaScriptコード実行
- サーバー環境への不正アクセス
- 内部情報の漏洩
- サーバーの乗っ取り

**問題の確認方法**: 
ユーザー名を`#{console.log(process.env)}` などに設定し、サーバー環境変数が漏洩するか確認する。

**修正案**:
- `eval()`の使用を完全に避ける
- チャレンジ目的であれば、安全なサンドボックス環境(vm2など)でコード実行を制限する
- ユーザー入力の厳格なバリデーションを行う

### 3. CSRF(クロスサイトリクエストフォージェリ)に対する不十分な保護

**問題**: ユーザープロファイル更新機能にCSRF対策が不十分です。特定の条件でCSRF防御がバイパス可能です。

**場所**: `/routes/updateUserProfile.ts` の26-30行目:

challengeUtils.solveIf(challenges.csrfChallenge, () => {
  return ((req.headers.origin?.includes('://htmledit.squarefree.com')) ??
    (req.headers.referer?.includes('://htmledit.squarefree.com'))) &&
    req.body.username !== user.username
})


**重大度**: Medium

**潜在的な影響**:
- 攻撃者は被害者のブラウザを使用して、被害者が知らない間にプロファイル情報を変更できる
- 特に管理者アカウントがターゲットになった場合、特権昇格につながる可能性がある

**問題の確認方法**:
特定のドメイン(htmledit.squarefree.com)からのリクエストでCSRFが機能するかテストする。

**修正案**:
- CSRFトークンを実装し、すべてのPOST/PUT/DELETE操作で検証する
- SameSite Cookieポリシーを適用する
- Content-Typeヘッダーの検証を追加する

### 4. 不適切なエラー処理

**問題**: エラー発生時に詳細な情報が含まれたエラーメッセージが返される場合があります。

**場所**: 複数箇所、例えば `/routes/userProfile.ts` の42行目:

next(new Error('Blocked illegal activity by ' + req.socket.remoteAddress))


**重大度**: Low

**潜在的な影響**:
- 攻撃者にシステム情報が漏洩する
- セキュリティ制御のバイパスに役立つ情報を提供する

**問題の確認方法**:
不正なリクエストを送信し、返されるエラーメッセージを確認する。

**修正案**:
- ユーザー向けのエラーメッセージを一般化し、詳細情報を含めない
- IPアドレスなどの技術的情報はログに記録するが、レスポンスには含めない

## 推奨されるベストプラクティス

1. **入力バリデーション**:
   - すべてのユーザー入力に対して厳格なバリデーションを実装する
   - 特に、SQLやテンプレートエンジンに渡される前にデータをサニタイズする

2. **安全なAPIの使用**:
   - 生のSQLクエリを避け、ORM機能やパラメータ化クエリを使用する
   - `eval()`などの危険な関数の使用を避ける

3. **認証・認可の強化**:
   - CSRF対策の完全な実装(トークン検証など)
   - 認証済みユーザートークンの適切な管理
   - セッション管理の強化

4. **エラー処理とロギング**:
   - 一般的なエラーメッセージをユーザーに表示し、詳細情報はログに記録する
   - エラーロギングを適切に実装し、セキュリティインシデントを追跡できるようにする

5. **定期的なセキュリティテスト**:
   - 静的コード分析ツールの使用
   - 定期的な脆弱性スキャン
   - ペネトレーションテストの実施

なお、OWASP Juice Shopは意図的にセキュリティ脆弱性を含むよう設計されたアプリケーションであるため、本レビューで指摘した問題点は教育目的として組み込まれている可能性があります。実際の運用環境では、これらの脆弱性は早急に修正すべきものです。

結果・評価

プレイドではセキュリティ組という有志で集まったプロダクト横断・部門横断でプロダクトセキュリティに取り組むバーチャル組織があり、そこでレビューを行い、どのようなリスクを検知できるか・過去行ったセキュリティレビューで見つかったリスクが見つけられるかを検証しました。

結果として、コード単体で特定できるようなよく知られた脆弱性パターン・入力値検証・ログ出力などについては、有効な指摘が得られました。

一方でロジックが複雑なパターン、認証・認可が絡む箇所については、表面的な分析で推測してまい精度が下がる印象で、網羅性も十分とは言えないです。

現状では人手によるレビューが不可欠ですが、AIを補助的に活用することで、レビュープロセスの効率化、既知の問題点の早期発見、開発者のセキュリティに関する学習・意識向上を促進する上で、価値があると感じています。

精度を高め、より深い分析を実現するためには、基本となるプロンプトの改善に加え、以下の取り組みが重要になると考えます。

  • プロダクト固有の仕様や設計ドキュメント、過去のレビュー結果から得られる知見(傾向や重点レビュー観点など)をナレッジとして蓄積・活用
  • 脅威分析やデータフロー分析といった、より俯瞰的な分析アプローチとの連携

終わりに

AIエージェントを使用したセキュリティレビューの試みについて紹介しました。

妥当性や網羅性についてはまだ改善が必要ですが、セキュリティレビュー時の補助として、特に既知のパターンに基づいた脆弱性の発見や、単純なチェック漏れの指摘に有効な手段となり得ると感じています。

今後の展望としては、前章で述べた改善をしつつ、レビューを開発へ組み込んで早期発見・コード品質全体の向上をしていきたいと考えています。