1 Ruby on Rails とセキュアプログラミング
長谷川 武
1. Ruby on Rails
著名なWebアプリケーションフレームワークのひとつにRuby on Rails がある。Ruby on Rails は、プログラミング言語 Ruby を対象として作られたWebアプリケーションフレームワークである。2004年7月に初めて登場し、2010年8月29日にはバージョン3が正式リリースされた。[1]
Ruby on Rails は、多くの機能を備えた部類のフレームワークである。主に次の機能を備えている。
※ サードパーティから入手できる認証・認可用のプラグインモジュールを追加して実現する。Ruby on Rails はそのための枠組みをもっている。 |
Ruby on Rails は、Ruby言語がもつ拡張性の高さを利用して作られている。Rubyでは、実行時にクラスの機能を拡張することができる。そのことを利用して、新しい「指令」をいくつも定義し、ひとつのクラスをドメイン固有言語(DSL)の処理系として使うことができる。プログラミングの場面では、そのようにして定義された Ruby on Rails 特有の「指令」を多数用いることになる。
2. Ruby on Rails 上のセキュアプログラミング
Ruby on Railsはバージョン2から、SQL注入等の著名な脆弱性への対応が考慮され始めたと言われている。それは、現在の最新版(バージョン3)においても引き継がれ、拡充されている。
Ruby on Rails のセキュアプログラミング機能、およびセキュリティの考慮事項には、主に次のものがある。[2][3]
b) スクリプト注入(XSS)対策 c) リクエスト強要(CSRF)対策
e) Taintを利用した入力値追跡
g) アクセス認可 これらのうち、(1)~(4) が、Ruby on Rails そのものが備えているものであり、(5)~(7) は、サードパーティによるプラグイン等が提供している機能である。 |
2.1 SQL注入対策
Ruby on Rails では(バージョン2以降)変数バインディング機能を用いることで、不用意な文字列連結によるSQL注入脆弱性の発生を回避できるようになっている。
例 (1) r1 = Persons.all(:conditions => ["name = ?", params[:x]]) (2) r2 = Persons.where("name = ?", params[:x])
上記のうち、(1) がバージョン2、(2) がバージョン3のスタイルである。
これらの変数バインディングの実装であるが、Ruby on Rails の中のデータベース・アダプタ・クラスにおいて、それぞれのデータベース・ソフトウェアに応じた特殊文字のエスケープ処理を設けることによって実現されている。データベース・ソフトウェアの prepared statement 機能は利用されていない。
ところで、文字列連結によるSQL文の組み立てはAPI の上では禁止されていないので注意が必要である。例えば、次のような書き方は避けるべきである。
避けるべき例 (1) r1 = Persons.all(:conditions => ["name = '#{params[:x]}'"]) (2) r2 = Persons.where("name = '#{params[:x]}'")
以前から Ruby on Rails には、ActiveRecord と呼ばれるO/Rマッパーがバンドルされてきたが、Ruby on Rails 3 では、この ActiveRecord にも改造が施された。データベース・クエリに ARel と呼ばれるドメイン固有言語(DSL)が導入されたので ある。
ARel のプログラミングは、従来のようにSQL文全体を一度に記述するのではなく、select, from, where, group といった、SQLの構成要素に対応したメソッドを呼び出して、Relation クラスのインスタンスの上にクエリの条件を追加してゆくスタイルをとる。
ARel の導入で、例えば、次のようなメソッドが追加されている。
(1) Rails 2 までに存在していたメソッド
- find, first, last, all, exists?, count_by_sql, find_by_sql,
create, update, update_all, delete_all, destroy_all 等
(2) Rails 3 から追加されたメソッド
- select, from, where, group, having, includes, joins,
limit, order, reorder,
create_with, eager_load, preload
average, calculate, count, maximum, minimum, sum 等
ARel の導入にともなって増やされたメソッドにおいても、変数バインディングの機能が設けられているので、常にそれを用いることを推奨する。
クエリを断片ごとに記述する ARel のメソッド呼び出しは、見た目は異なっていても、相変わらずSQL文を操作しているのだという意識をもち、SQL注入脆弱性に用心する必要がある。
2.2 スクリプト注入対策
Ruby on Rails 2 では、出力ページのERBテンプレート(rhtmlファイル)に例えば次のような記述をすることによって、& " < > の記号にエスケープ処理を施した上で値を出力させることができる。
Rails 2 の例 <%= h @name %>
この h メソッドは、ERB::Util クラスの html_escape メソッドの別名である。
Ruby on Rails 3 では、ERBテンプレートにおける h メソッドの適用がデフォルトになった。
Rails 3 の例 <%= @name %>
何も書かなければ、h メソッドによるエスケープ処理が適用される。これに伴い、逆に、このエスケープ処理を敢えて行わせないための raw メソッドが導入された。
Rails 3 の例(推奨はしない) <%= raw @name %>
このhメソッドによるエスケープ処理は多くの箇所で効果を発揮するものの、タグの属性値を出力する際値を必ず引用符で囲む("...")配慮が必要である。また、次の箇所では対策効果が無いかまたは弱く、出力値にさらなる制約を設ける必要がある。:
- style="..." 属性 - onMouseOver="..." 等のイベントハンドラ属性 |
2.3 リクエスト強要(CSRF)対策
Ruby on Rails ではリクエスト強要(CSRF)対策の、フォームへのトークンの埋め込みとフォームデータを受信した際のトークンの照合が自動で行われる。
2.3.1 トークンの自動埋め込み
form_for 等のメソッドで生成するフォームには、自動でリクエスト強要(CSRF)対策のトークンが自動で埋め込まれる。Rails 2 ではトークンの固定値をプログラマが指定する形態が存在したが、Rails 3 では、処理系がランダムな値を生成するよう変わっている。
例えば、form_for メソッドでフォームを記述すると:
<%= form_for @foo do |f| %> ...
次のように、フォーム内にauthenticity_tokenという名前のトークンが埋め込まれる。:
<form action="/foos" class="new_foo" id="new_foo" method="post"> <input name="authenticity_token" type="hidden" value="JWI+bZ1tsj8dhTmBC8ZOV+PYGlfhKQwP2u0b9ehCCcg=" /> ...
2.3.2 トークンの自動照合
コントローラクラスのビジネスロジックが呼び出される前に、リクエスト強要(CSRF)対策のトークンの照合が自動で行われる。ただし、照合が行われるのは次のようなHTTPリクエストの場合である。
(1) | POSTで送信されるフォームデータに限られる。 |
(2) | protect_from_forgery 指令によって指定されたコントローラクラスとメソッドの範囲。デフォルトの保護対象は、すべてのコントローラクラスの全メソッド(ただしPOSTで呼ばれるもの)である。 |
照合が行われ、トークンの値が適切でないと、
- ActionController::InvalidAuthenticityToken
という例外が発生し、当該HTTPリクエストの処理は中断される。
この、リクエスト強要(CSRF)対策のトークン自動照合は、Ruby on Rails が備えているbefore_filter という機能を用いて実現されている。この before_filter を用いると、ビジネスロジックと、そのビジネスロジックの呼出をWebクライアントに許すか否かのセキュリティロジックとを分離して配置することが可能である。
所定のセキュリティロジックを配置しておけば、プログラマはビジネスロジックの記述に集中することができ、また、ソースコードをシンプルに保つことができる。
2.4 入力検査
Ruby on Rails アプリケーションの「モデル」クラス群には、項目ごとの入力値チェックを指定できるvalidates 指令および validates_xxx 指令群が用意されている
Ruby on Rails 2 では、例えば、次のような validates_xxx 指令群を用いて入力値検査を指定することができる。
- validates_presence_of :name
(項目 name の値が空でないこと) - validates_length_of :password, :minimum => 8, :maximum => 20
(項目 password の値が8桁以上20桁以下であること)
- validates_format_of :email, :with => /\A[-_.\w]+@[-\w]+\.[-.\w]+\z/
(項目 email の値が当該正規表現にマッチすること)
- validates_inclusion_of :gender, :in => %w( m f )
(項目 gender の値が m または f のみであること)
- validates_uniqueness_of :customer_code, :case_sensitive => false
(項目 customer_code がDBテーブル中で重複しないこと。大文字小文字は区別せず比較する) 等
Ruby on Rails 3 からは、validates 指令が加わった。ひとつの入力項目の複数の検査仕様を、ひとつの validates 指令を使って記述できるようになった。
- validates
この validates 指令および validates_xxx 指令群は、アプリケーションの「モデル」部分、すなわちActiveRecord 継承クラスに記述する。検査は値がデータベースへの格納の前に行われる。
データベースに格納されるデータの仕様への合致は確保できるが、「モデル」のどれかのクラスのフィールドを、データベースに格納する前に参照し、何らかの形で利用する場合、独自の入力検査ロジックを書く必要がある。
2.5 Taint機能
Ruby 言語には Taint 機能が備わっている。Taint 機能とは、オブジェクトにマークをつけ、信頼できない入力値の伝播を追跡できる仕組みである。
Ruby on Rails 自体は Taint 機能を利用していないが、Taint 機能を用いたプラグインをつくることが可能である。
Ruby on Rails 2 用には、Taint 機能を利用したスクリプト注入(XSS)対策プラグインとして SafeERB というものが存在している。
2.6 ユーザ認証
Ruby on Rails 本体には、ユーザ認証の仕組みは用意されていない。しかし、ユーザ認証のための複数のプラグインがサードパーティから提供されている。
サードパーティによるユーザ認証プラグインの例 [4]
|
2.7 アクセス認可
Ruby on Rails 本体には、アクセス認可の仕組みは用意されていない。しかし、アクセス認可のための複数のプラグインがサードパーティから提供されている。
2.7.1 所有者やロールによる認可
情報の所有者やロールにもとづくアクセス認可のプラグインには、例えば、CanCan、declarative_authorization、grant 等がある。[5]
-
コード CanCanの権限記述例
# models/ability.rb class Ability include CanCan::Ability def initialize(user) user ||= User.new # guest user if user.role? :admin can :manage, :all else can :read, :all can :create, Comment can :update, Comment do |comment| comment.try(:user) == user || user.role?(:moderator) end if user.role?(:author) can :create, Article can :update, Article do |article| article.try(:user) == user end end end end end |
2.7.2 ログインによる保護
未ログイン者からコンテンツを保護するしくみは、ユーザ認証プラグインがサポートしていることが多い。
2.7.3 before_filter によってコンテンツを保護する
Ruby on Rails アプリケーションの「コントローラ」クラス群(HTTPリクエストに応じた処理を行う)では、before_filter という指令が使える。これは、「コントローラ」クラス群のビジネスロジックが呼び出される前に、何らかのフィルタ処理メソッドの呼び出すよう定義するものである。これを用いると、ビジネスロジックとアクセス認可のためのコードをきれいに分離して配置できる。ユーザ認証やアクセス認可のプラグインの多くは、これを利用している。
以上
参考文献・参照プロジェクト
[1] | "Ruby on Rails 3.0 Release Notes" http://edgeguides.rubyonrails.org/3_0_release_notes.html |
[2] | Heiko Webers "Ruby on Rails Security Updated" http://www.slideshare.net/heikowebers/ruby-on-rails-security-updated-rails-3-at-railswaycon |
[3] | Heiko Webers "Ruby On Rails Security Guide" http://guides.rubyonrails.org/security.html |
[4] | 認証プラグインのプロジェクト(主なもの) Device - https://github.com/plataformatec/devise authlogic - https://github.com/binarylogic/authlogic restful-authentication - https://github.com/technoweenie/restful-authentication clearance - https://github.com/thoughtbot/clearance ruby-openid - https://github.com/openid/ruby-openid |
[5] | 認可プラグイン・プロジェクト CanCan - https://github.com/ryanb/cancan declarative_authorization - https://github.com/stffn/declarative_authorization grant - https://github.com/nearinfinity/grant |
参考資料
木村,「Ruby On Rails Security Guideの訳 : 概要 + 1.Introduction」 http://www.flatz.jp/archives/1942 |
|
松田明 ,「Rails 3を支える名脇役たち その1 - Arel -」 http://gihyo.jp/dev/serial/01/ruby/0043 |
|
「Rubyリファレンスマニュアル - ERBクラス」 http://www.ruby-lang.org/ja/man/html/erb.html |