SideCI Blog

自動コードレビューサービスSideCIを提供している株式会社アクトキャットのコーポレートブログです。



Brakeman を使って Rails プロジェクトのセキュリティインシデントを防ぎましょう

Rails の登場によってRubyが世界中のWeb開発に使われるようになっています。Rails はとても高機能で便利なフレームワークですが、何でもやってくれる安心感からか、セキュリティに気を配らずに実装されてしまうケースも見受けられます。適当な書き方をしてしまうと、 容易にセキュリティインシデントの原因となるコードを産んでしまうでしょう。

そこで使ってみたいのが Brakeman になります。セキュリティに重点を置いたRailsプロジェクトの静的解析ツールになります。

Brakeman のインストール

Brakeman は Rubygems でインストールできます。

$ gem install brakeman

もちろん Rails プロジェクトの Gemfile に記述してインストールも可能です。

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

Brakeman の使い方

使い方は brakeman コマンドをRailsプロジェクトを指定して実行するだけです。

$ brakeman /path/to/rails/project

そうすると解析が実行されて結果が出ます。例えば以下のような結果が出ます。リダイレクトであったり、ビューに関する警告が出ています。

+BRAKEMAN REPORT+

Application path: /path/to/rails/project
Rails version: 4.2.8
Brakeman version: 3.6.2
Started at 2017-06-11 16:22:29 +0900
Duration: 0.357863 seconds
Checks run: BasicAuth, BasicAuthTimingAttack, ContentTag, CreateWith, CrossSiteScripting, DefaultRoutes, Deserialize, DetailedExceptions, DigestDoS, DynamicFinders, EscapeFunction, Evaluation, Execute, FileAccess, FileDisclosure, FilterSkipping, ForgerySetting, HeaderDoS, I18nXSS, JRubyXML, JSONEncoding, JSONParsing, LinkTo, LinkToHref, MailTo, MassAssignment, MimeTypeDoS, ModelAttrAccessible, ModelAttributes, ModelSerialize, NestedAttributes, NestedAttributesBypass, NumberToCurrency, QuoteTableName, Redirect, RegexDoS, Render, RenderDoS, RenderInline, ResponseSplitting, RouteDoS, SQL, SQLCVEs, SSLVerify, SafeBufferManipulation, SanitizeMethods, SelectTag, SelectVulnerability, Send, SendFile, SessionManipulation, SessionSettings, SimpleFormat, SingleQuotes, SkipBeforeFilter, StripTags, SymbolDoSCVE, TranslateBug, UnsafeReflection, ValidationRegex, WithoutProtection, XMLDoS, YAMLParsing


+SUMMARY+

+-------------------+-------+
| Scanned/Reported  | Total |
+-------------------+-------+
| Controllers       | 14    |
| Models            | 7     |
| Templates         | 23    |
| Errors            | 0     |
| Security Warnings | 8 (1) |
+-------------------+-------+

+----------------------+-------+
| Warning Type         | Total |
+----------------------+-------+
| Cross Site Scripting | 5     |
| Dynamic Render Path  | 2     |
| Redirect             | 1     |
+----------------------+-------+


+SECURITY WARNINGS+

+------------+-------------------+----------+----------------------+------------------------------------------------------------------------------------------------------------------->>
| Confidence | Class             | Method   | Warning Type         | Message                                                                                                           >>
+------------+-------------------+----------+----------------------+------------------------------------------------------------------------------------------------------------------->>
| High       | OauthsController  | callback | Redirect             | Possible unprotected redirect near line 14: redirect_to(+Bot.find_by_domain(request.host.split(".")[0]).admin_edit>>
| Medium     | ScriptsController | show     | Cross Site Scripting | Unescaped model attribute rendered inline near line 9: render(text => +ChatLog.find_by_uniq_key(params[:id]).bot.c>>
+------------+-------------------+----------+----------------------+------------------------------------------------------------------------------------------------------------------->>


View Warnings:

+------------+------------------------------------------------+----------------------+------------------------------------------------------------------------------------------------->>
| Confidence | Template                                       | Warning Type         | Message                                                                                         >>
+------------+------------------------------------------------+----------------------+------------------------------------------------------------------------------------------------->>
| Medium     | shared/_bot_header (Template:admin/bots/index) | Cross Site Scripting | Unsafe model attribute in link_to href near line 12: link_to(+current_user.bots.find_by_domain(r>>
| Medium     | shared/_bot_header (Template:admin/bots/index) | Cross Site Scripting | Unsafe model attribute in link_to href near line 17: link_to(+current_user.bots.find_by_domain(r>>
| Medium     | docs/show (DocsController#show)                | Dynamic Render Path  | Render path contains parameter value near line 4: render(partial => "shared/doc_#{+params[:id].g>>
| Medium     | docs/show (DocsController#show)                | Dynamic Render Path  | Render path contains parameter value near line 9: render(partial => "shared/doc_#{+params[:id].g>>
| Weak       | bots/show (BotsController#index)               | Cross Site Scripting | Unescaped model attribute near line 17: markdown.render(strip_tags(+Bot.find_by_domain(request.h>>
| Weak       | bots/show (BotsController#index)               | Cross Site Scripting | Unescaped model attribute near line 24: markdown.render(strip_tags(+Bot.find_by_domain(request.h>>
+------------+------------------------------------------------+----------------------+------------------------------------------------------------------------------------------------->>

後はこの指摘に沿って修正していくだけです。

結果の内容は標準出力の他、HTML/JSON/CSV/TSV/Markdown/Code Climateに対応しています。それぞれ拡張子または -f オプションで指定します。複数のファイル出力に対応しているので拡張子で指定した法が分かりやすいです。

$ brakeman -o output.html -o output.json

f:id:a_nakatsugawa:20170629160716p:plain

対応するRailsのバージョンなど

Brakeman はRails 2.3 〜 5.x まで幅広く使います。Ruby 1.8の構文解析にも対応していますが、Ruby自体は1.9.3以上が必須となっています。

過去の結果と比較する

--compare オプションを使うと過去の結果と比較できます。これはJSONファイルを使うようなので、出力ファイルとしてJSONファイルを用意しておく必要があります。そうすると新しい問題が発生している(または直っている)ことが容易に確認できるようになります。

$ brakeman --compare  output.json
Checks finished, collecting results...
{
  "new": [
    {
      "warning_type": "Cross Site Scripting",
      "warning_code": 84,
      "fingerprint": "947ce5634858e206076c3d1f6379c11ccb62aa3430b427150975388a5d3c4a68",
      "check_name": "RenderInline",
      "message": "Unescaped parameter value rendered inline",
      "file": "app/controllers/bots_controller.rb",
      "line": 15,
      "link": "http://brakemanscanner.org/docs/warning_types/cross_site_scripting/",
      "code": "render(text => params[\"hub.challenge\"], { :layout => false })",
      "render_path": null,
      "location": {
        "type": "method",
        "class": "BotsController",
        "method": "index"
      },
      "user_input": "params[\"hub.challenge\"]",
      "confidence": "High"
    }
  ],
  "fixed": [

  ]
}

Rails プロジェクトが重たいときには

Rails はモノリシックなシステムになりがちで、規模が大きくなりやすいです。そのため Brakeman でのチェックも時間がかかるようになるかも知れません。そこで --faster オプションをつけて解析を高速化できます。これは高速にはなりますが、チェック項目が減るようです。

設定ファイルを作成する

Brakeman でも他の静的解析ツールと同様に設定ファイルでチェック項目の制御ができます。特徴的なのは警告や注意ごとに、それを無効にするかどうか対話で決められることでしょう。設定ファイルは config/brakeman.ignore に保存されます。生成する際には -I オプションを使います。

$ brakeman -I
  :
Checks finished, collecting results...
Filtering warnings...
Input file: |config/brakeman.ignore|            
No such file. Continue with empty config? y
1. Inspect all warnings
2. Hide previously ignored warnings
3. Prune obsolete ignored warnings
4. Skip - use current ignore configuration
?  1
--------------------
Actions:
i - Add warning to ignore list
n - Add warning to ignore list and add note
s - Skip this warning (will remain ignored or shown)
u - Remove this warning from ignore list
a - Ignore this warning and all remaining warnings
k - Skip this warning and all remaining warnings
q - Quit, do not update ignored warnings
? - Display this help
--------------------
Confidence: Weak
Category: Cross Site Scripting
Message: Unescaped model attribute
Code: markdown.render(strip_tags(Bot.find_by_domain(request.host.split(".")[0]).readme.to_s))
File: app/views/bots/show.html.erb
Line: 17
Action: (i, n, k, u, a, s, q, ?) i
--------------------
Ignoring 8 warnings
Showing 0 warnings
1. Save changes
2. Start over
3. Quit, do not save changes
?  1
Output file: |config/brakeman.ignore| 

この設定ファイルですべて警告を無効にすると警告は出なくなります。それではよくありませんので、内容を確認して早急に直すべきものは修正していきます。問題ないことが分かっているものについては無効にしておいても良いでしょう。


Rails はとても高機能なフレームワークですが、セキュリティを万全にしてくれる訳ではありません。これはどのようなプログラミング言語、フレームワークでも同じです。ちょっとしたミスがセキュリティインシデントにつながります。Brakemanを使うことでそうした問題のいくらかを発見、修正できるようになるでしょう。

SideCI の自動コードレビューでは Brakeman にも対応しています。個人であればローカルに Brakeman をインストールすれば済みますが、チームでの開発体制ではクラウドでチェックする方が効率的かつプロジェクト全体のコード品質向上につながるはずです。

SideCIでは無料トライアルも可能です。ぜひ皆さんのRailsプロジェクトでお試しください