monoの開発ブログ

Rubyで安全なWebアプリを作るためのメモ

RubyでWebアプリケーションを作るときにセキュリティ関連でやっておくべきことのメモです。

以下の4つの問題について、Sinatra・Hamlを使っている環境(うちの環境)での対策方法を説明しています。それぞれの問題についての詳細はここでは触れないので、徳丸本を読むとよいと思います。

  • XSS
  • CSRF
  • クリックジャッキング
  • IEのsniffing

XSS対策

エスケープ漏れを防ぐ

きちんとエスケープをする、というのは言葉にすると簡単ですが手作業で実現するのは極めて困難です。まともなテンプレートエンジンにはエスケープを自動で行う機能が付いているのでそれを利用しましょう。エスケープを自動化することで、デフォルトでエスケープを行い、指定があった場合のみエスケープしないようにすることができ、エスケープ漏れを最小限に抑えられます。

app.rb

class App < Sinatra::Base
  set :haml, :escape_html => true
end

index.haml

%p
  = "ここに書いた内容は自動でエスケープされます<script>alert(0);</script>"
%p
  != "こっちに書いた内容はエスケープされません<script>alert(0);</script>"

XSSがあったときの影響を抑える

そうは言ってもうっかりXSSを作り込んでしまうことがないとは言い切れません。X-XSS-Protectionヘッダを追加することで、ブラウザ側でXSSを検出したときに真っ白のページを表示してスクリプトの実行を防いでくれます。しかし、回避する方法が発見されることもあるので、あまり過信せず、エスケープ漏れをなくすよう努めた上で利用しましょう。

nginxの設定ファイルに以下のように記述することでヘッダを付加できます。

nginx

add_header X-XSS-Protection "1; mode=block";

Apache

Header set X-XSS-Protection "1; mode=block"

CSRF対策

rack_csrfというgemが用意されているので利用します。

$ gem install rack_csrf

rack_csrfでは、sessionとformにtokenを仕込んでおいて、postされたときにsessionとformの値が一致すれば正規の操作と判断します。もしformの値が間違っていたり、値が付加されていない場合はRack::Csrf::InvalidCsrfToken例外が発生するので、適当にエラーページを表示します。

app.rb

require 'rack/csrf'

class App < Sinatra::Base
  disable :session
  use Rack::Session::Cookie, :secret => 'ランダムな文字列'
  use Rack::Csrf, :raise => true

  helpers do
    def csrf_tag
      Rack::Csrf.csrf_tag(env)
    end
  end

  get '/' do
    haml :index
  end

  error Rack::Csrf::InvalidCsrfToken do
    ...
  end
end

index.haml

%form{:action => '/post', :method => 'POST'}
  ...
  != csrf_tag

クリックジャッキング対策

X-Frame-OptionsヘッダにDENYを設定しておくと、自サイトのコンテンツを外部サイトのフレーム内に表示できなくなるため、クリックジャッキング攻撃を防ぐことができます。IE8以降とその他の主要なブラウザが対応しています。

nginx

add_header X-Frame-Options DENY;

Apache

Header set X-Frame-Options DENY

IEのsniffing対策

IEはContent-Typeではなくファイルの内容からファイルの種類を判定するため、HTMLタグっぽい文字列が含まれたテキストファイルや画像ファイルがHTML扱いされてしまうことがあります。もしそのファイルにJavaScriptが含まれていた場合、XSSが発生してしまいます。IE7以下の場合はどうしようもありませんが、IE8以降であればX-Content-Type-Optionsヘッダにnosniffを指定することでsuniffingを防げます。

ただ、X-Content-Type-Optionsヘッダが指定されているとChromeの挙動が変わることがあるので注意が必要です。

nginx

add_header X-Content-Type-Options nosniff;

Apache

Header set X-Content-Type-Options nosniff