BrakemanでRailsの脆弱性を検知して防ぐ

今までもテストや監視を紹介してきましたが今回は脆弱性をとりあげてみます。幸いにもRails用にBrakemanというGemで機械的に脆弱性診断を行えます。これさえやっておけば完璧と断言できるわけではありませんが、人の手でやるには限界があるので導入しておくにこしたことはありません。

概要

  • Railsの脆弱性診断にBrakemanを使う
  • CI(GitHub Action)で自動で診断するようにする

Brakeman is何?

BrakemanはRails用の脆弱性診断Gemです。SQLインジェクションなどの代表的な脆弱性を始め、様々な脆弱性を静的解析してくれます。これをやっておけば完璧、ということではありませんが転ばぬ先の杖として定期的に診断しておくと安心です。

期待すること

もしもうっかり脆弱性を含んだ実装をしてしまった場合、リリースする前やマージする前に気づいて脆弱性を含んだアプリケーションをリリースしてしまうことを防ぎたい、というのがモチベーションです。

実際やったこと

ずBrakemanをインストールします。普通にgem installしたり、RailsのGemfileに書いたり、Dockerで走らせたり、といろいろありますが、今回はシンプルにGemfileに書いていこうと思います。

group :development do
  gem 'brakeman', require: false
end

ですね。そうしたらいつもどおり bundle installを実行します。これで入ったはずなのでとりあえず実行してみましょう。

$ bundle exec brakeman
# =>
Loading scanner...
Processing application in /path/to/rails_project
Processing gems...
[Notice] Detected Rails 6 application
Processing configuration...
[Notice] Escaping HTML by default
Parsing files...

みたいな感じで実行され、結果がでると思います。例えばここで、あえてSQLインジェクションの可能性を持つコードを書いてみましょう。

User.where("name = '#{params[:name]}'")

こんな感じ。それで再度Brakemanを回すと

$ bundle exec brakeman
# =>
Loading scanner...
# 中略...
== Warning Types ==

SQL Injection: 1

== Warnings ==

Confidence: High
Category: SQL Injection
Check: SQL
Message: Possible SQL injection
Code: User.where("name = '#{params[:name]}'")
File: app/controllers/home_controller.rb
Line: 4

のように表示されます。想定どおり検出されました。

自動化する

せっかく検出できることがわかりましたが手動で実行するのは心もとないですね。

できれば自動でチェックするのが良いでしょう。

パっと思いつく手段としてはGit hooksのpre-commitやpre-pushでやるか、CIでやるかでしょうか。

CIでできるのであれば個人的に前者はお勧めしません。Git hooksは—no-verifyオプションで回避できますし、個人の設定如何で変わってしまうので担保にするには弱いです。

ではCIでやることにしましょうか。幸い、最近のGitHubではGitHub Actionが使えますのでそれでやってみます。

.github/workflows/brakeman.ymlを作り

name: brakeman
on:
  - pull_request
jobs:
  brakeman:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions/setup-ruby@v1
        with:
          ruby-version: '2.7'
      - run: gem install --no-document bundler
      - run: bundle install --quiet --jobs 4 --retry 3
      - run: bundle exec brakeman -Aqw1 --no-pager

と書きます(runで指定しているオプションは一例なので必要に応じて変更してください)

次にGitHubにpushしPull Requestを作ります。そうするとこのYAMLに従って、GitHub Actionsが走り、最終的に

と表示されます。

もちろん誤検出や大丈夫なケースもありますし、逆にBrakemanでは検出できない脆弱性もあると思うので工夫が必要ですが、ひとまずこれで疑わしきコードの混入に気づきやすいようになったはずです。

今回触れなかったこと

上にも書いたようにBrakemanの設定オプションにはあまり触れませんでした。—helpで見るか

に全てオプションが書いてあります。

また、

には日本語化されたドキュメントもあります。

感想

今回はBrakemanによる脆弱性診断とその自動化をしてみました。

一人で個人開発をしているとQAも限界がありますし、そのための労力を減らすため静的解析や自動化を上手く使い開発に集中したいですね。

LinterやTest、エラー監視の導入と一緒にBrakemanもRails Templateに入れてしまって、新しいプロジェクトを始めるときに常に入るようすると尚良さそうです。

来た道、行く道 LifeJourney[39.3/2021-01](月次振り返り)Sentryを入れてRailsのエラー監視ををする(sentry-ruby, sentry-rails)