こんにちは!Webアプリの開発や、会社のIT管理を任され始めた新人の皆さん、日々の業務本当にお疲れ様です。
「セキュリティ」って聞くと、なんだか難しそうな暗号や、映画に出てくるようなハッカーの黒い画面を想像して身構えてしまいますよね。でも大丈夫です。セキュリティの基本は、私たちが普段暮らしている現実世界の「防犯」と同じなんです。
今回は、Webアプリの裏側でこっそり起こる「Mass Assignment(マス・アサインメント)脆弱性」という、ちょっと名前がカッコいいけれど恐ろしいバグについて、身近な例えを交えながら優しく紐解いていきたいと思います。一歩ずつ、一緒に学んでいきましょう!
—
1. 家の鍵に例える「Mass Assignment」の正体
まずは、想像してみてください。あなたが新しいマイホームを建てたとします。
玄関のドアには、しっかりとした鍵がついていますよね。この鍵を開けられるのは、家族(あなたや配偶者)が持っている「マスターキー」だけです。
ところが、もし工務店のうっかりミスで、「郵便受けの小さな隙間から手を入れたら、家の中のどんな部屋の扉の鍵も自由に変えられるレバー」が放置されていたらどうでしょう?泥棒がそこから手を入れて、「子供部屋」の鍵を「誰でも出入り自由」に書き換えてしまったら……大変なことになりますよね。
Webの世界における「Mass Assignment」も、これと全く同じことが起きています。
データの「何でも受け入れちゃう窓口」が危険!
Webアプリを作るとき、ユーザーから送られてきたデータ(お名前、住所、メールアドレスなど)を、プログラムがまとめてドカンとデータベースに保存する仕組みをよく使います。
例えば、ユーザーのプロフィール画面(名前や自己紹介文を変える画面)を考えてみましょう。
本来なら、変更していいのは「自己紹介文」や「アイコン画像」くらいのはずです。しかし、プログラムが「送られてきたデータは、データベースのユーザー表の列に、全部そのまま当てはめちゃえ!」という大雑把な作りになっていると、どうなるでしょうか。
攻撃者は、ブラウザの裏側の通信をちょっと細工して、「管理者権限フラグ(is_admin = true)」という、普通は書き換えてはいけない秘密のデータをこっそり送り込んできちゃいます。
プログラムがそれを疑うこともなく、「おっ、送られてきたからそのまま登録しとくね!」とデータベースを書き換えてしまうと……。
なんと、ただの一般ユーザーだったはずの人が、一瞬でアプリの「最高管理者(GOD権限)」に昇格してしまうのです!これが、Mass Assignment脆弱性の恐ろしいメカニズムです。
—
2. 実際のコードで見てみよう(危ない書き方と直した書き方)
百聞は一見にしかず。PHPのような言語を例にして、具体的にどう危なくて、どう直せばいいのかを見ていきましょう。
危険なコード:何でもかんでもそのまま保存しちゃう例
以下のコードは、「ユーザーがフォームに入力したデータを、中身をチェックせずにそのままデータベースに突っ込んでいる」危険な状態の例です。
<?php
// 【危険な例】ユーザーから送られてきた配列をそのままデータベースに保存
function updateProfile($userId, $postData) {
// $postDataの中身には、name(名前)やbio(自己紹介)のほかに、
// 悪意あるユーザーがこっそり混ぜた is_admin(管理者権限) が入っているかもしれない!
$user = User::find($userId);
// 送られてきたデータを丸ごとデータベースの更新に使ってしまう(マス・アサインメント)
$user->update($postData);
return "プロフィールを更新しました!";
}
?>
この書き方だと、悪意あるリクエスト(例: ?name=hacker&is_admin=1 のようなパラメータ)を投げられたら一発アウトです。
—
3. 解決策:DTO(Data Transfer Object)と「ホワイトリスト」で泥棒をシャットアウト!
じゃあ、どうやってこの泥棒を防げばいいのでしょうか?
現実世界で例えるなら、「郵便受けから手が入らないようにガッチリ塞ぐ」こと、そして「家の中に入れるのは、あらかじめ名前がリストに書いてある家族(信頼できるデータ)だけにする」ことです。
この「信頼できるデータだけを入れる仕組み」を、プログラミングの世界ではDTO(Data Transfer Object:データ転送オブジェクト)や、通称「ホワイトリスト方式」と呼びます。
安全なコード:通していい項目を厳しく決める例
変えていい項目を「名前」と「自己紹介」だけにガチガチに制限してみましょう。
<?php
// 【安全な例】DTO(専用の入れ物)を使って、変更を許可する項目を絞り込む
class ProfileUpdateDto {
public string $name;
public string $bio;
public function __construct(array $inputData) {
// ホワイトリスト方式:受け取るべきデータだけを明示的に指定する
// 万が一、is_adminなどが混ざっていても、ここでは完全に無視(スルー)されます!
$this->name = $inputData['name'] ?? '';
$this->bio = $inputData['bio'] ?? '';
}
}
function updateProfileSecure($userId, array $rawPostData) {
// 1. まず専用のDTO(入れ物)に通して、余計なデータを綺麗にふるい落とす
$dto = new ProfileUpdateDto($rawPostData);
$user = User::find($userId);
// 2. 許可された安全なデータだけを使ってデータベースを更新する
$user->update([
'name' => $dto->name,
'bio' => $dto->bio,
// is_admin はここに含まれていないので、絶対に書き換わらない!
]);
return "安全にプロフィールを更新しました!";
}
?>
このように、「アプリ側が受け取っていいデータを、あらかじめ厳しくリスト化しておく(ホワイトリスト化)」ことが、Mass Assignmentを防ぐための最も強力で確実な盾になります。
—
4. フレームワークの便利機能にも注意しよう!
最近のモダンなWebフレームワーク(LaravelやRuby on Rails、Springなど)には、開発を楽にするために「送られてきたデータを自動でモデルに詰め込む便利な魔法」があらかじめ備わっています。
例えば Laravel なら $request->all()、Rails なら Params をそのままパカーッとモデルに渡すような書き方ですね。
これらはコード量が減って非常に便利ですが、「防犯の鍵をかけ忘れた状態の自動ドア」になりやすい諸刃の剣でもあります。
フレームワークを使うときは、必ず次のような設定を意識してください。
- ブラックリスト方式(Fillable / Guarded の設定):
「この項目だけは絶対に書き換えさせないぞ!」というフィールド(例: guarded = ['is_admin', 'id'])をモデルに必ず設定する。
- 基本はホワイトリスト(Validated):
バリデーション(入力値チェック)を通過した安全なデータ($request->validated() など)だけを使う癖をつける。
—
まとめ:一歩ずつ、安全なコードを書く楽しさを知ろう
今回は、Mass Assignmentという少しマニアックだけど実務でめちゃくちゃ重要な脆弱性と、その対策としてのDTO・ホワイトリスト制御についてお話ししました。
- Mass Assignmentとは: 意図しないデータまで一括で書き換えてしまう、データの「おせっかいな自動受け入れ」による脆弱性。
- 対策(ホワイトリスト): 「これしか通しません!」とあらかじめ許可リスト(DTOなど)を作り、悪意あるパラメータをシャットアウトする。
セキュリティの対策は、一度コツを掴んでしまえば「お、きれいなコードでしっかり守れたぞ」というエンジニアとしての達成感や楽しさに繋がります。
新人の皆さんも、日々の開発の中で「このデータ、ユーザーが勝手に書き換えても大丈夫なやつだっけ?」と一瞬立ち止まるクセをつけて、頼れるセキュリティ・センスを磨いていってくださいね。
それでは、次の解説記事でもまた一緒に楽しく学んでいきましょう!
コメント