マスアサインメントとは?起きるパターンと対策法

マスアサインメントとは?起きるパターンと対策法

#セキュリティ#Web開発#脆弱性対策

マスアサインメントは、リクエストボディに余計な項目を足すだけで、更新を想定していない属性まで書き換えられる脆弱性です。 対策の勘所は、外部から更新してよい属性を許可リストで明示できているかどうかです。

マスアサインメントとは

マスアサインメント(mass assignment):リクエストのパラメータをまとめてオブジェクトの属性に一括代入する仕組みで、想定していない属性まで外部入力で更新できてしまう脆弱性です。 ORMの一括代入の受け口を無制限にしていることが根本原因になります。

たとえばプロフィール更新のリクエストに"role":"admin""is_verified":trueを足すと、画面には無い項目まで書き換わります。 権限の昇格、課金状態や所有者の改ざんなど、更新させるつもりのなかった値を操作されます。 画面上は無害な更新機能でも、モデルが内部に持つ属性が多いほど、外部から狙える対象は広がります。

マスアサインメントが起きるパターン

典型的な弱点は次のとおりです。

  • リクエストのパラメータを丸ごとモデルに渡し、一括で属性を更新している
  • 画面のフォームには存在しない属性でも、パラメータに含めれば代入されてしまう
  • 新規作成や更新のエンドポイントで、受け入れる項目を絞らずオブジェクト全体を更新している
  • APIで、内部管理用のフラグ(権限、状態、作成者ID)まで外部から書き換えられる
  • 自動生成されたCRUDのエンドポイントを、受け入れ項目を絞らずそのまま公開している
  • ネストしたオブジェクトや関連先まで、まとめて一括代入の対象になっている

共通するのは「外部から渡された項目を無選別に受け入れ、更新してよい属性を制限していない」という点です。

対策法

原則は「外部から更新を許す属性を許可リストで明示し、それ以外は受け付けない」ことです。

  • 受け入れる属性をホワイトリストで定義する(RailsのStrong Parameters、DTOやスキーマでの項目限定など)。これが本命の対策になる
  • 権限や状態、所有者IDなど内部管理用の属性は、一括代入の対象から必ず外す
  • 作成用と更新用で許可する項目を分け、役割ごとに受け入れる範囲を変える
  • 変更できる属性の一覧をコードで明示し、レビューで意図しない項目が混じらないか確認する

禁止する項目を個別に除外する方式は追加時に漏れやすく、本命は許可する項目だけを通すホワイトリストです。 作成と更新のエンドポイントを洗い出し、受け入れる属性を明示する実装に統一するのが費用対効果の高い進め方になります。

Rails での事例

Rails はこの脆弱性への標準対策として Strong Parameters を持っています。 params を未許可のままモデルに渡すと ActiveModel::ForbiddenAttributesError になるため、素直に書いているかぎり穴は開きません。 ただし、Strong Parametersを回避した場合だけでなく、permitの許可範囲が広すぎる場合にも事故は起きます。

# 危険その1:permit! は「全属性を許可」の意味
def update
  current_user.update!(params.require(:user).permit!)
end

# 危険その2:素の Hash は Strong Parameters の保護対象外
def update
  attrs = JSON.parse(request.raw_post)
  current_user.update!(attrs)
end

# 危険その3:permit を使っていても、内部管理用の role を許可している
def update
  attrs = params.require(:user).permit(:name, :email, :role)
  current_user.update!(attrs)
end

2つ目と3つ目は、とくに見落とされがちです。 Strong Parameters が守るのは ActionController::Parameters を渡したときだけで、JSON.parse が返す素の Hash はチェックなしで一括代入されます。 API 実装でリクエストボディを自前でパースしていると、この形になりやすくなります。 一方、permitを通していても、roleのような内部管理用属性を許可リストへ含めれば、その属性は正規の入力として代入されます。

直し方は、どの経路でも permit で属性を列挙することです。

def update
  current_user.update!(user_params)
end

private

def user_params
  params.require(:user).permit(:name, :email, :bio)
end

roleis_verified のような内部管理用の属性は、この許可リストに入れないことがそのまま防御になります。 管理者だけが変更できる属性は、admin_user_params のように別メソッドへ分け、許可の範囲をエンドポイントの権限と対応させます。

まとめ

マスアサインメント対策の要点を整理します。

  • 根本原因は「外部から渡された項目を無選別に受け入れていること」。画面上は無害な更新機能でも、モデルが内部に持つ属性の数だけ狙える対象がある
  • 本命の対策は、更新を許す属性の許可リスト化。RailsのStrong Parameters、DTOやスキーマでの項目限定など、「許可したものだけを通す」方式に統一する
  • 権限(role)、状態、所有者IDなど内部管理用の属性は、一括代入の対象から必ず外す。ここを含めた瞬間に垂直方向の権限昇格が成立する
  • 作成用と更新用、一般ユーザー用と管理者用で許可リストを分け、受け入れる範囲をエンドポイントの権限と対応させる
  • Railsでは、permit!による全属性許可、JSON.parseが返す素のHashの直接代入、広すぎるpermitのいずれでも事故が起きるため、Strong Parametersを使っている事実ではなく、実際に何を許可しているかを確認する

禁止する項目を個別に除外する方式は、モデルにカラムが増えるたびに漏れます。点検は作成・更新のエンドポイントを洗い出し、permitで属性を列挙しているかを確認するところから始めてください。

関連して読める記事

マスアサインメントは「書き込み側」の属性単位の認可漏れで、その対になるのが読み取り側の過剰なデータ露出です。OWASP API Security Top 10の2023年版では、この2つは同じ項目(BOPLA)に統合されています。許可リストにroleを含めてしまえばそのまま垂直方向の権限昇格になり、他人のリソースを書き換えられればIDORと同じ現象になります。

過剰なデータ露出(Excessive Data Exposure)とは?起きるパターンと対策法

過剰なデータ露出の仕組みと起きるパターン、シリアライザ層で返す属性を列挙する対策を整理します。render json: @user が password_digest まで返す危険例、filter_attributes や Devise では守れない範囲、関連モデルの include による芋づる露出、本人と他人でシリアライズを分ける実装を具体的なコードで示します。

2026年7月25日

権限昇格とは?起きるパターンと対策法

権限昇格(水平方向・垂直方向)の仕組みと起きるパターン、サーバ側での権限判定とデフォルト拒否を軸にした対策を整理します。画面でメニューを隠すだけの危険な実装と、管理系コントローラを共通の基底クラスへ集約する方法、Pundit の verify_authorized による掛け忘れ検出までを具体的なコードで示します。

2026年7月25日

IDOR(安全でない直接オブジェクト参照)とは?起きるパターンと対策法

IDOR(安全でない直接オブジェクト参照)の仕組みと起きるパターン、所有者チェックを軸にした対策を整理します。RailsのOrder.find(params[:id])がそのまま穴になる理由と、current_user起点の取得への直し方を具体的なコード例で示します。OWASP API Security Top 10で1位のBOLAに相当する脆弱性です。

2026年7月23日
この記事をシェア