ふと思いついたネタを。単純なネタですがこの点に関する注意をしている記事を見た記憶がないので。
もくじ
■概要
scaffoldは気をつけて使わないと、ユーザから上書きされてはいけないフィールドが書き換えられる恐れあり。scaffoldは便利だけど、そのままではすこし危険。
■scaffoldは便利だけど…
とりあえずRuby on Railsが有名だと思うのでRuby on Rails(バージョン2.2.3)で書きますが、grailsでも同様の問題がありました。
さて、まずはscaffoldしないと話になりません:
% rails test % cd test % script/generate scaffold User name:string password:string is_admin:boolean
としてモデル・コントローラ・ビューを自動生成できますよね。このうち、おそらく管理者かどうかをチェックするためにつけたと思われる「is_admin」が、そのままだと一般ユーザから書き換え放題になります。
どうやるかって?簡単です。newアクションのviewはこうなりますよね。
この時の、生成されるhtmlのうち:
<p> <label for="user_is_admin">Is admin</label><br /> <input id="user_is_admin" name="user[is_admin]" type="checkbox" value="1" /><input name="user[is_admin]" type="hidden" value="0" /> </p>
のチェックボックスの<input>とその後のhiddenな<input>が並んだところをコピーして控えておきます。この部分は、このアプリだけでなく他のアプリを攻撃する際にも使い回せます。(name属性だけ書き換えて再利用します。)
さて。実際のアプリにおいて、is_adminは管理者が設定するフラグであって、ユーザ側から選択できてはマズいですから、削除すると思います。こんな感じで:
<h1>New user</h1> <% form_for(@user) do |f| %> <%= f.error_messages %> <p> <%= f.label :name %><br /> <%= f.text_field :name %> </p> <p> <%= f.label :password %><br /> <%= f.text_field :password %> </p> //-------------ここから削除------------------ <p> <%= f.label :is_admin %><br /> <%= f.check_box :is_admin %> </p> //-------------ここまで削除------------------ <p> <%= f.submit "Create" %> </p> <% end %> <%= link_to 'Back', users_path %>
そうするとこんな感じの画面になって、一見ユーザからは管理人であるかどうかは選択できなくなりますよね:
実際に、このまま普通に作っても、「Is admin: 」となって、falseになっていることが分かります:
ところがどっこい、動的にHTMLを書き換えてさっきメモした<input>のコードを動的にフォームの中にコピーすると管理者になれちゃいます!w
FirefoxのFirebugや、Chromeのデベロッパーツールを使えば簡単にHTMLを動的に書き換えることできます。プラグインをインストールする必要が無くて楽なChromeで試してみます。
デベロッパーメニューはこちらにあります†1:
次に、動的に書き換えるためにformのところをクリックして、「Edit as HTML」を選びます:
すると編集できるようになるので、先程の<input>のコードを<form>のすぐ次くらいにでも張り付けます:
これで終わりです。適当にエディット領域外をクリックして編集を終了すると…
チェックボックスが出てきます。あとはご想像のとおり。これを黒くチェックして、適当にIDとパスワードを入力すると…
はい、管理者ユーザになれました。
まあたしかに、is_adminという名前が分からないと書き換えはできないので、ソースコードを公開してないアプリなら即危ないというわけではありませんが、セキュリティ的にはやっぱりマズイと思います。特にオープンソースアプリ…大丈夫でしょうか?
■対策
Userコントローラのcreateアクションのうち、
@user = User.new(params[:user])
この部分を、
@user = User.new(params[:user]) @user.is_admin = false
にするとか(今確認したらredmineだとこうしてる)、
@user = User.new(:name=> params[:user][:name], :password=> params[:user][:password])
として、そのアクションで何を書き換えるのかをはっきりさせる、とかでしょうか。
■なんだかなあ
すごく単純なミスですが、実を言うと今まで全く気づかなかったので今回掲載しました^^;UFO大好き霊子さんにもこのミスがあります。証明写真作成工房は修正しておきました。
出力されるformの中身を自動的にチェックして、formの中身以外のがリクエストに混入してたらアウトって出来ないのかなあ?その方がrailsっぽいよね。Ajaxで動的にフォームを生成するときは、そのための設定をするとか。
ソースコード読んだら修正できるだろうか?
- †1: Dev channelなので、多少画面はお手持ちのChromeと異なる可能性があります