#前提
セキュリティについて勉強した方が今後の為になると教えをいただいた為、学んだことを書いていきます。
#本題
フレームワークは、Webアプリを容易に開発できるよう作られた。
Ruby on Railsには、セキュリティを保つための便利なヘルパーメソッドが用意されている。
だが、導入するだけでセキュリティを保てるものではない。
Gartner Groupによると、攻撃の75%がWebアプリケーション層に対して行われていると見積もっており、監査を受けた300のWebサイトのうち97%が脆弱性を抱えているという結果を得ているとのこと。
これは、Webアプリケーションに対する攻撃は比較的行いやすく、一般人も理解や操作が可能なほどにWebアプリケーションがシンプルであるため。
安全なWebアプリケーションを開発するために必要なことは、
・すべての階層を最新の状態に保つこと
・敵を知ること
#セッション
セッションはある種の攻撃の対象になることがある。
##セッションとは
アプリケーションはセッションを用いて、多くのユーザーがアプリケーションとやりとりできるようにしつつ、各ユーザー固有のステートを維持。
たとえば、、、
セッションを用いることで、ユーザーが認証を1回行うだけで以後のリクエストでサインインしたままにできる。
Railsでは、アプリケーションにアクセスするユーザーごとにセッションオブジェクトを1つ提供する。
ユーザーが既にアプリケーションを利用中であれば、Railsは既存のセッションを読み込み、その他の場合は新しいセッションを作成する。
##セッションハイジャック
ユーザーのセッションIDが盗まれると、攻撃者がそのユーザーをかたってWebアプリケーションを利用できてしまう。
多くのWebアプリケーションには何らかの認証システムがある。
ユーザーがユーザー名とパスワードを入力すると、Webアプリケーションはそれらをチェックして、対応するユーザーIDをセッションハッシュに保存し、その後、そのセッションは有効になる。
リクエストが行われるたびに、Webアプリケーションはセッションで示されたユーザーidを持つユーザーを読み込む。
このときに再度認証を行なう必要はない。
セッションは、cookie内のセッションidによって識別できる為。
このように、cookieはWebアプリケーションに一時的な認証機能を提供している。
要するに、他人のcookieを奪い取れれば、その奪い取ったユーザーの権限でWebアプリケーションを使うことができる!
###セッションハイジャックの手法・対策
セキュリティに不備のあるネットワークではcookieを覗き見することができる。
無線LANは、まさにそのようなネットワークの一例。
接続されているクライアントのすべてのトラフィックをのぞき見ることは、暗号化されていない無線LANでは特に簡単に行行える。
Webアプリケーションの開発者にとっては、これはSSLによる安全な接続の提供が必要である。
Rails 3.1以降では、アプリケーションの設定ファイルでSSL接続を強制することによって達成できる。
config.force_ssl = true
・よく目立つログアウトボタンを提供する
なぜなら、、、
わざわざcookieを消去するようなユーザーはいないため。
ログアウトを忘れてしまうと、次のユーザーがそのWebアプリをそのまま使えてしまうため。
クロスサイトスクリプティング (XSS) 攻撃は、多くの場合、ユーザーのcookieを手に入れるのが目的。
・攻撃者が自分の知らないcookieをわざわざ盗み取る代りに、自分が知っているcookieのセッションidを固定してしまうという攻撃方法がある
##セッションストレージ
RailsはデフォルトのセッションストレージとしてActionDispatch::Session::CookieStoreを用いる。
RailsのCookieStoreはクライアント側のcookieにセッションハッシュを保存する。
サーバーはこのセッションハッシュをcookieから取得することで、セッションIDの必要性を解消。
こうすることで、アプリケーションのスピードは著しく向上するが、このストレージオプションについては議論の余地があるため、セキュリティ上の意味やストレージでの制約について以下の点を十分考えておかなければならない。
・cookieの上限は4KB。セッションに関連するデータを保存する目的でのみcookieを使う。
・cookieはクライアント側に保存される。
クライアントには、cookieの期限が切れた場合にもcookieの内容が残っていることがある。
また、クライアントのcookieが他のコンピュータにコピーされる可能性もある。
セキュリティ上重要なデータをcookieに保存することは避けるべき。
・cookieは本質的に一時的な情報である。
サーバーはcookieに期限を設定できるが、期限が切れる前にcookieやcookieの内容がクライアント側で削除される可能性がある。
恒常性の高いデータは、すべてサーバー側で永続化すべき。
・セッションcookieはひとりでに失効することはないため、悪用目的で使い回される可能性もある。
保存済みのタイムスタンプを利用して古いセッションcookieをアプリケーションで失効させるのもよい方法かも。
・Railsはcookieをデフォルトで暗号化する。
クライアントは暗号を解読しないかぎりcookieの内容を読み取ることも編集することもできない。
秘密情報を適切に扱っていれば、cookieのセキュリティは一般的に保たれていると考えてよい。
・CookieStoreはセッションデータの保管場所をencrypted cookie jarで安全に暗号化する。
これにより、cookieベースのセッションの内容の一貫性と機密性を同時に保つ。
暗号化鍵は、signed cookieに用いられる検証鍵と同様に、secret_key_base設定値から導出される。
秘密鍵は十分に長く、かつランダムなものにしなければならない。
一意な秘密鍵を得るにはrails secretを使用する。
暗号化済みcookieと署名済みcookieで使うsalt値を同じにしないことも重要。
複数のsalt設定に異なる値ではなく同じsalt値を使ってしまうと、別のセキュリティ機能で同じ鍵が導出されてしまい鍵の強度が落ちる可能性がある。
test環境とdevelopment環境のアプリケーションでは、アプリケーション名からsecret_key_baseを導出。
それ以外の環境では、必ずconfig/credentials.yml.encにあるランダムな鍵を使わなければならない。
##CookieStoreセッションに対する再生攻撃
再生攻撃(replay attack)の仕組み
・ユーザーがクレジットを受け取る。総額はセッションに保存されているとする。
・ユーザーがクレジットで何かを購入する。
・つかった分減ったクレジットがセッションに保存される。
・ここでユーザーの暗黒面が発動。最初にブラウザに保存されていたcookieをコピーしてあったものを、現在のブラウザのcookieと差し替える。
・ユーザーのクレジット額が元に戻る。
この種のデータはセッションではなくデータベースに保存するのが最善。
この再生攻撃は、セッションにnonce (1回限りのランダムな値) を含めておくことで防ぐことができる。
nonceが有効なのは1回限りであり、サーバーはnonceが有効かどうかを常に追跡し続ける必要がある。
複数のアプリケーションサーバーで構成された合いの子アプリケーションの場合、状況はさらに複雑になる。
nonceをデータベースに保存してしまうと、せっかくデータベースへのアクセスを避けるために設置したCookieStoreを使う意味がなくなってしまう。
##セッション固定攻撃
セッション固定 (session fixation) とは
ユーザーのセッションIDを盗む代りに、攻撃者が意図的にセッションIDを既知のものに固定するという方法。
この攻撃では、ブラウザ上のユーザーのセッションIDを攻撃者が知っているセッションidに密かに固定しておき、ブラウザを使うユーザーが気付かないうちにそのセッションIDを強制的に使わせる。
この方法であれば、セッションIDを盗み出す必要すらない。
###セッション固定攻撃対策
最も効果的な対応策は、ログイン成功後に古いセッションを無効にし、新しいセッションidを発行すること。
これなら、攻撃者が固定セッションidを悪用する余地はない。
この対応策は、セッションハイジャックにも有効。
Railsで新しいセッションを作成する方法
reset_session
ユーザー管理用にDeviseなどのgemを導入している場合、ログイン・ログアウト時にセッションが自動的に切れるようになっている。
もし自分で管理する場合は、ログイン後 (セッションが作られた後) にセッションを切るように注意しなければならない。
上のメソッドを実行するとセッションにあるすべての値が削除されるため、それらの値を新しいセッションに転送しておく必要がある。
その他対応策
セッションにユーザー固有のプロパティを保存しておき、ユーザーからリクエストを受けるたびに照合して、マッチしない場合はアクセスを拒否するという方法。
ユーザー固有のプロパティとして利用可能な情報には、リモートIPアドレスや user agent (= webブラウザの名前) があるが、後者は完全にユーザー固有とは限らない。
IPアドレスを保存して対応する場合、インターネットサービスプロバイダ (ISP) や大企業からのアクセスはプロキシ越しに行われていることが多いことを思い出しておく必要がある。
IPアドレスはセッションの途中で変わる可能性があるため、IPアドレスをユーザー固有の情報として使おうとすると、ユーザーがWebアプリケーションにアクセスできなくなったり、ユーザーの利用に制限が加わる可能性がある。
##セクションの期限切れ
セッションを無期限にすると、攻撃される機会を増やしてしまう (クロスサイトリクエストフォージェリ (CSRF)、セッションハイジャック、セッション固定など)。
セッションIDを持つcookieのタイムスタンプに有効期限を設定するという対応策も考えられなくはありません。
しかし、ブラウザ内に保存されているcookieをユーザーが編集できてしまう点は変わらないので、やはりサーバー側でセッションを期限切れにする方が安全。
データベーステーブルのセッションを期限切れにするには、たとえば次のようにSession.sweep("20 minutes")を呼ぶと、20分以上経過したセッションが期限切れになる。
class Session < ApplicationRecord
def self.sweep(time = 1.hour)
if time.is_a?(String)
time = time.split.inject { |count, unit| count.to_i.send(unit) }
end
delete_all "updated_at < '#{time.ago.to_s(:db)}'"
end
end
攻撃者が5分おきにセッションを維持すると、サーバー側でセッションを期限切れにしようとしてもセッションを恒久的に継続させることができてしまう。
これに対する単純な対策は、セッションテーブルにcreated_atカラムを追加すること。
これで、期限を過ぎたセッションを削除できる。
上のsweepメソッドで以下のコードを使用。
delete_all "updated_at < '#{time.ago.to_s(:db)}' OR
created_at < '#{2.days.ago.to_s(:db)}'"
#クロスサイトリクエストフォージェリ(CSRF)
この攻撃方法は、ユーザーによる認証が完了したと考えられるWebアプリケーションのページに、悪意のあるコードやリンクを仕込むというもの。
そのWebアプリケーションへのセッションがタイムアウトしていなければ、攻撃者は本来認証されていないはずのコマンドを実行できてしまう。
多くのRailsアプリケーションがcookieベースのセッションを使っている。
この時、セッションIDをcookieに保存してサーバー側にセッションハッシュを持つか、すべてのセッションハッシュをクライアント (ブラウザ) 側に持つ。
どちらの場合にも、ブラウザはリクエストのたびにcookieを自動的にドメインに送信する (そのドメインで利用できるcookieがある場合)。
ここで問題となるのは、異なるドメインに属するサイトからリクエストがあった場合にもブラウザがcookieを送信してしまうという点。
###CSRFの対応方法
1、第一に、W3Cが要求しているとおり、GETとPOSTを適切に使う。
2、GET以外のリクエストにセキュリティトークンを追加することで、WebアプリケーションをCSRFから守ることができる。
HTTPプロトコルは2つの基本的なリクエストであるGETとPOST(DELETE、PUT、PATCHはPOSTと同様に使われるべきです)を提供している。
World Wide Web Consortium (W3C) は、HTTPのGETやPOSTを選択する際のチェックリストを提供している。
以下の場合はGET
・そのやりとりが基本的に問い合わせである場合 (クエリ、読み出し操作、検索のような安全な操作)
以下の場合はPOST
・そのやりとりが基本的に命令である場合
・そのやりとりによってユーザーにわかる形でリソースの状態が変わる場合 (サービスへの申し込みなど)
・そのやりとりによって生じる結果をユーザーが把握できる場合
WebアプリケーションがRESTfulであれば、PATCH、PUT、DELETEなどのHTTPメソッドも使われているだろう。
しかし、一部のブラウザはこれらのメソッドをサポートしていない。
確実にサポートされているのはGETとPOSTだけ。
Railsでは_methodという隠しフィールドを使ってこれらのHTTPメソッドをサポートしている。
POSTリクエストも (意図に反して) 自動的に送信されることがありえる。
ブラウザのステータスバーに、www.harmless.com というWebサイトへのリンクが表示されているとする。
そしてこのリンクに仕掛けがあり、POSTリクエストをこっそり送信する新しいフォームを動的に作成するようになっているとする。