最近、Railsの認証プラグインのデファクトスタンダード(多分)であるrestful_authenticationを使い始めました。まだ少ししか触れてないですが、かなり便利そうな感じです。このプラグインは、認証に必要なコードを自動生成してくれるのですが、そこで少し気になるところが出てきました。UsersController#create*1のコードのコメント部分です。
def create logout_keeping_session! @user = User.new(params[:user]) success = @user && @user.save if success && @user.errors.empty? # Protects against session fixation attacks, causes request forgery # protection if visitor resubmits an earlier form using back # button. Uncomment if you understand the tradeoffs. # reset session self.current_user = @user # !! now logged in redirect_back_or_default('/') flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code." else flash[:error] = "We couldn't set up that account, sorry. Please try again, or contact an admin (link is above)." render :action => 'new' end end
とりあえず適当に訳すと、「Userをcreateするときに、reset sessionするとsession fixation attackは防げるけど、いったん戻ってもう一度submitボタンを押した時なんかにrequest forgery protectionが行われるよ(そしてエラーが出るよ)。トレードオフだよ。」と書いてあるようです。
session fixation attackとは
Wikipediaみたら例が3つあったのでそれぞれ意訳してみました。
例その1(A simple attack scenario)
下記のような設定のサイトhttp://unsafe/があるとする。
- どんなセッションidも受け付ける
- セッションidはクエリの文字列で設定する
- validationはなし
- マロリーはアリスに、http://unsafe/?SID=I_WILL_KNOW_THE_SIDというリンク付きのメールを送って、アリスに下記リンクを踏ませ、ログインさせた。
- マロリーはhttp://unsafe/?SID=I_WILL_KNOW_THE_SIDにアクセスすることで、アリスとしてアクセスすることができる。
- 悪さしほうだい。
例その2(Attack using server generated SID)
例その1は、クエリの文字列でどんなセッションIDも受け取れるから起こる問題で、サーバが作ったセッションIDだけ受け付けるようにすれば安全なんじゃないの?→そうでもないよ。クエリでセッションを受け取るのが問題なんだよ。という例。
- マロリーはhttp://vulnerable/にアクセスして、0D6441FEA4496C2というセッションIDを受け取った。
- マロリーはアリスにhttp://vulnerable/?SID=0D6441FEA4496C2というリンク付きのメールを送ってリンクを踏ませる。アリスがログインすると、ログインセッションが0D6441FEA4496C2になる。
- あとは、マロリーが同じセッションを使えば、アリスとしてアクセスする事が出きる。
- 悪さしほうだい
例その3(Attacks using cross-site cooking)
別ドメインのクッキーを保存させるブラウザの脆弱製を利用した攻撃の例。
- マロリーはアリスにhttp://evil/のurlを含んだメールを送って踏ませる。
- そこでアリスはI_WILL_KNOW_THE_SIDというセッションIDを「http://good/のドメインで」得る。
- マロニーはまたアリスにメールして、今度はhttp://good/にアクセスさせ、さらにログインさせる。
- アリスがログオンしている間、マロリーはI_WILL_KNOW_THE_SIDのセッションIDを利用する事でアリスとしてhttp://good/にアクセスする事が出来る。
- 悪さしほうだい
session fixation attackまとめ
訳してから気づいたのですが上記の例はあんまり良くなかった気がします・・・
まとめると、「あるサイトで同じセッションをずーっと使い回しているのを利用して、なんとか自分で設定したセッションIDを他人に使わせる→同じセッションIDを使って他人になりすます」という攻撃なようです。なので、セッションの使い回しをせずに定期的に作り直すようにすれば防げそうです。そこで、restful_authenticationはログイン部分にreset_sessionが書かれている(コメントアウトでですが)わけですね。でもそうするとrequest forgery protectionにひっかかるそうな。
request forgery protectionとは?
cross site request forgery(通称CSRF)を防ぐための認証です。
CSRFとは?
詳しくはこのエントリを参照のこと。
簡単に言うと、あるサイトにログイン中のユーザに別のサイトでURLを踏ませて、ユーザの知らぬ間にデータを操作させてしまうというものです。それを防ぐには、決められた場所からのみ操作することを許可すればよいです。Railsはauthenticity tokenと呼ばれるトークンをフォームに自動で埋め込むことでそれを実装しています。このトークンはユーザのセッションと比較されます。同値の時のみ操作が許可されます。
上記のコードのreset session部分*2をコメントアウトして実行すると、セッションが作り直されます。しかし、戻るボタンを押して戻ったページに埋め込まれているauthenticity tokenは当然変更されるはずがないので、request forgery protectionの認証は失敗します。
感想
クッキーセッションなら、セッションの変更はクッキーとしてブラウザに反映されるので、セッションが変化しないという脆弱製をつくsession fixation attackは使えないはず。なのでクッキーセッション使用特は問題無さそうな気がします。他のセッション格納方法を使用している場合は、おそらく一度作られたセッションIDは削除されるまで固定になるはずなので、session fixation attackから守るのを優先するかrequest forgery protectionを優先するかを選ぶ必要がありそうです