Brakemanによる静的解析での脆弱性診断
概要
Webアプリケーションのセキュリティを確保することは、開発者にとって最も重要な責務の一つです。しかし、複雑なコードベースの中から、SQLインジェクション、クロスサイトスクリプティング(XSS)、マスアサインメントといった脆弱性を手動で見つけ出すのは非常に困難です。
Brakemanは、Railsアプリケーション専用に設計された静的解析セキュリティ脆弱性スキャナです。アプリケーションのソースコードを直接スキャンし、潜在的なセキュリティリスクを報告してくれます。コードを実行する必要がないため、開発の早い段階で、かつ網羅的に脆弱性をチェックできるのが大きな利点です。
この記事では、Brakemanの導入方法、基本的な使い方、そしてCI/CDパイプラインへの組み込みについて解説します。
Brakemanの導入
BrakemanはGemとして提供されています。Gemfile
の:development
グループに追加するのが一般的です。
Gemfile
group :development do
gem 'brakeman'
end
bundle install
を実行してインストールします。
Brakemanの基本的な使い方
使い方は非常にシンプルです。Railsアプリケーションのルートディレクトリで、以下のコマンドを実行するだけです。
$ brakeman
Brakemanはプロジェクトのコードをスキャンし、発見された脆弱性をレポートとして出力します。
レポートの例
+-----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Confidence | Category | Warning Type | Message | File | Line | Code |
+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+
| High | SQL Injection | SQL Injection | Possible SQL injection | app/controllers/users_controller.rb | 10 | User.where("name = '#{params[:name]}'") |
| Medium | Cross-Site Scripting | Unescaped Output | Unescaped parameter value | app/views/users/show.html.erb | 5 | <%= params[:name] %> |
+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+-----------------+
レポートには以下の情報が含まれます。
- Confidence: 脆弱性である可能性の確信度(High, Medium, Low)。
- Category: 脆弱性のカテゴリ(例: SQL Injection)。
- Warning Type: 警告の種類。
- Message: 警告メッセージ。
- File: 問題が検出されたファイル。
- Line: 問題があるコードの行番号。
- Code: 該当するコードスニペット。
主な警告の種類
Brakemanが検出できる代表的な脆弱性は以下の通りです。
- SQL Injection: ユーザー入力をSQLクエリに直接埋め込むことで、データベースを不正に操作される脆弱性。
- Cross-Site Scripting (XSS): ユーザー入力をエスケープせずにHTMLに出力することで、悪意のあるスクリプトが実行される脆弱性。
- Mass Assignment:
update_attributes
などで、意図しない属性まで更新されてしまう脆弱性。 - Command Injection: ユーザー入力をシェルコマンドに直接埋め込むことで、任意のコマンドが実行される脆弱性。
- Redirect: ユーザー入力に基づいてリダイレクトを行う際に、外部の悪意のあるサイトに誘導される脆弱性。
レポートのフォーマット変更
デフォルトのコンソール出力以外にも、様々なフォーマットでレポートを出力できます。
HTMLレポート:
-o
オプションで出力ファイルを指定します。bash$ brakeman -o brakeman_report.html
ブラウザで開ける見やすいレポートが生成されます。
JSONレポート:
bash$ brakeman -o brakeman_report.json
他のツールと連携させる場合に便利です。
偽陽性 (False Positive) の管理
静的解析ツールは、実際には脆弱性ではないコードを「脆弱性の可能性がある」と報告すること(偽陽性)があります。Brakemanには、これを管理するための仕組みがあります。
インタラクティブな無視:
-I
オプションを付けて実行すると、各警告に対して無視するかどうかを対話形式で選択できます。bash$ brakeman -I
選択結果は
config/brakeman.ignore
ファイルに保存されます。設定ファイルによる無視:
config/brakeman.ignore
ファイルが生成されたら、次回のスキャンからはこのファイルに記載された警告は無視されます。json{ "ignored_warnings": [ { "warning_type": "Cross-Site Scripting", "fingerprint": "...", // 警告を一意に識別するハッシュ "note": "This is intentionally unescaped for a specific reason." } ] }
note
フィールドに無視する理由を記述しておくことが強く推奨されます。
CI/CDへの組み込み
Brakemanの真価は、CI/CDパイプラインに組み込み、コードがマージされる前に自動的に脆弱性スキャンを実行することで発揮されます。
GitHub Actionsでの実行例
# .github/workflows/security.yml
name: Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
brakeman:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2' # プロジェクトのRubyバージョンに合わせる
bundler-cache: true
- name: Run Brakeman
run: bundle exec brakeman --exit-on-warn --no-progress
--exit-on-warn
: 何か一つでも警告(無視していないもの)があれば、0以外のステータスコードで終了します。これにより、CIのジョブが失敗し、脆弱性のあるコードのマージを防ぎます。
まとめ
Brakemanは、Rails開発におけるセキュリティのベースラインを確保するための非常に強力なツールです。
- 手軽さ: コマンド一つで簡単に実行できる。
- 網羅性: Rails特有の脆弱性を幅広くカバーしている。
- 自動化: CI/CDに組み込むことで、継続的な脆弱性スキャンを実現できる。
もちろん、Brakemanが全ての脆弱性を発見できるわけではありません。しかし、多くの一般的なセキュリティリスクを開発の初期段階で、かつ自動的に検出してくれることは、開発者にとって大きな助けとなります。安全なRailsアプリケーションを構築するために、ぜひBrakemanを開発プロセスに取り入れてください。