マスアサインメントとは?
マスアサインメントは特定のフレームワークを使用する時、HTTPリクエストを通じてクライアントから渡されたパラメータの値をプログラムコード内の変数やオブジェクトの属性に自動的に割り当てる機能を指します。これにより、エンジニアはコードの簡潔性を高め、各パラメータの値を手動でオブジェクトに割り当てる手間を大幅に減らせます。
マスアサインメントの例: Ruby on Rails
Ruby on Rails(バージョン7基準)は、このマスアサインメントの機能を利用して、1行のコードで簡単にパラメータの値をモデルの属性に割り当てることができるメカニズムを提供します。
例えば、ユーザーが会員登録フォームを通じて提出したデータはコントローラーに渡され、User
モデルの属性にマッピングされます。
View
# app/views/users/new.erb
<%= form_with(model: @user, url: users_path, method: "post") do |form| %>
<%= form.label :email, "メール" %>
<%= form.email_field :email %>
<%= form.label :password, "パスワード" %>
<%= form.password_field :password %>
<%= form.submit "会員登録" %>
<% end %>
Controller - マスアサインメントを使用しない場合
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
@user = User.new
@user.email = params[:user][:email]
@user.password = params[:user][:password]
if @user.save
redirect_to success_page_path
else
render :new
end
end
end
Controller - マスアサインメントを使用する場合
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
@user = User.new(params[:user])
if @user.save
redirect_to success_page_path
else
render :new
end
end
end
マスアサインメントの脆弱性
マスアサインメントの手軽さの裏には、深刻なセキュリティリスクが存在します。例えば、ユーザーモデルに管理者権限を付与するis_admin
フィルドがある場合は、悪意のあるユーザーがフォームデータを操作して自分のアカウントに管理者権限を付与することができます。
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
@user = User.new(params[:user]) # is_admin: trueキー値ペアが含まれています。
if @user.save
redirect_to success_page_path
else
render :new
end
end
end
上記のコードでコントローラーはクライアントから渡されたparams[:user]
ハッシュにある全てのキー値ペアをUser
インスタンスに割り当てます。この時、悪意のあるユーザーがHTTPリクエストを操作してis_admin
の値をtrue
に設定すると、コントローラーはこれをそのままデータベースに保存し、悪意のあるユーザーは簡単に管理者権限を取得することができます。
ウェブブラウザのようなクライアント環境はいつでもアクセスして開発者ツールやスクリプトなどでデータを操作することができるので、ここで悪意のあるユーザーは簡単にHTTPリクエストを操作することができます。例えば、元の会員登録フォームにはemail
とpassword
フィールドだけが公開されていますが、悪意のあるユーザーは次のような方法でis_admin
フィールドを追加することができます。
-
HTML操作でリクエスト
開発者ツールのElementsパネルで
is_admin
のinput
タグを追加後、フォームを送信してサーバーにリクエストを送ることができます。<input type="hidden" name="user[is_admin]" value="true">
-
JavaScript操作でリクエスト
開発者ツールのConsoleパネルで
is_admin
のフォームデータを生成し、fetch APIでサーバーにリクエストを送ることができます。// JavaScriptを利用してフォームデータを生成 let formData = new FormData(); formData.append('email', 'minimabot@example.com'); formData.append('password', 'secure_password'); formData.append('is_admin', 'true'); // fetch APIでリクエスト fetch('/sign_up', method: 'POST', body: formData }) .then(response => response.json()) .then(data => console.log(data)) .catch((error) => console.error('Error:', error));
-
直接HTTPリクエスト
開発者ツールのNetworkタブでフォーム送信のHTTPリクエストを分析します。そして、curlのようなツールでパラメータに
is_admin
を含めt直接サーバーにリクエストを送ることができます。curl -X POST -d "email=minimabot@example.com&password=secure_password&is_admin=true" https://example.com/sign_up
対応: Strong Parameter
Railsはこのような脆弱性を補強するためにRails4バージョンからStrong Parametersを導入しました。これにより、エンジニアは許可されたパラメータのみを明示的に宣言し、予期しないマスアサインメントを防ぐことができます。
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
redirect_to success_page_path
else
render :new
end
end
private
def user_params
# 許可されたパラメータのみ
params.require(:user).permit(:email, :password)
end
end
上記のuser_params
メソッドは Strong parameters の良い例です。permit
メソッドを使って、email
とpassword
フィールドのみを許可し、他の全てのフィールドは無視されるか拒否されます。
結論
一括割り当ては開発の効率を高める便利な機能ですが、慎重に使う必要があります。RailsのStrong Parametersのようにフレームワークが提供するセキュリティ機能を正確に理解して適切に活用しましょう。 そうすれば、コードの簡潔性だけでなく、ウェブアプリケーションのセキュリティを大幅に向上させることができます。
参考