Eloquentの複数代入のリスク

タグ: Laravel5.1LTS   Laravel  

この記事はLaravel Advent Calendar 2015の2015年12月24日公開分です。

Laravelもだんだんと人気を博し、段々と有名になってきました。新しい人がどんどん増えているはずですが、その割にはいろいろなサイトで行われる質問は多くありません。

日本語の情報も豊富と言える段階に入ったのだと信じたいところです。

最近、なぜEloquentに複数代入の制限が導入されたのか、説明を目にすることが無くなりました。基本ですし、セキュリティリスクを生まないために知っておくべきことですので、改めて書こうと思います。

Laravelは簡単!

兎にも角にも、Laravelの人気の出始めは「簡単」であることが売りでした。Eloqunentも現在と同じく余計な定義を使わずに、すぐに使用できるORMとして人気を得始めました。Laravel3の頃の話です。

公式ドキュメントは今と同様に、主なポイントについて記述されていましたが、具体的に同アプリケーションを構築するかは書かれていませんでした。そのためのヒントは、ユーザーにより書かれたブログチュートリアルだけが頼りでした。

Laravelの人気と共に、チュートリアル記事も増えていきました。皆、Laravelの「簡単さ」を強調するチュートリアルでした。

フォームの入力項目名=データベースのカラム名

フォームの入力名とデータベースのカラム名を同じにして、入力値をデータベース(もしくはORMがインスタンスのモデル)へ一気に代入してしまおうと言うアイデアは、別にLaravelが最初というわけでありません。

しかしながら、Eloquentのモデルクラスの内部は空っぽなのに、データベーステーブルの操作ができることを示すのは、とても印象深かったのです。

よくあるチュートリアルは、ブログポストですので、こんなフォームが用意されていました。

フィールド 入力内容
title タイトル
body 記事本文

そしてデータベースはフィールドと同じカラムを持ちます。

カラム タイプ
id INTEGER
title VARCHAR(100)
body VARCHAR(255)

入力はInput静的クラスです。当時は、静的クラスを使っていました。

Post::create(Input::all());

なんてことでしょう。これで、入力をテーブルに保存できるんです。:D

csrfフィルター

でも、すぐにこれは動かなくなります。csrfフィルター、今のミドルウェアが実装されたからです。フォームにはCSRFトークンが埋め込まれます。Input::all()はトークンも入力されます。それをそのままEloquntのモデルへ入れようとすると、トークンが余計な項目ですから、エラーになりました。

マニュアルに紹介されたコードは次のようなものです。

$inputs = $Input::only('title', 'body');
Post::create($inputs);

それでもcsrfフィルターをルートに指定せずに、「ほれ、簡単だぜ」タイプのチュートリアル記事はWeb上に残っています。そこでドキュメントにも「必要な項目だけ、渡さないとセキュリティーリスクになる」ことが注意喚起されました。

典型的な例

一番危険なのはユーザー情報更新です。よく、自分の情報は自分で変更可能にしますよね。最低限度、パスワードの更新程度は提供しますよね。

あるページにパスワードだけを入力できるようにしましょう。フィールド名はpasswordです。

usersテーブルは典型的にはこんなものでしょう。

カラム タイプ
id INTEGER
username VARCHAR(255)
passwrod VARCHAR(255)
email VARCHAR(255)
role INTEGER

通常のユーザーはroleが0、管理者は1としましょうか。

ブラウザに表示されるのはpasswordフィールドだけですが、ブラウザのF12キーを叩き、ちょっといじれば入力項目を追加できます。ブラウザ使わなくてもcURLなどのツールやら、本当に悪意があればWebにアクセスするツールも作成できます。

そうした手段でroleフィールドを追加し、値に1を入れます。

このページによるユーザー情報の更新コードが以下のようになっていたら…

User::create(Input::all());

見事に、自分を管理者に昇格できます。管理者は通常、サイトの設定やら情報を変更できる強い権限を持っていますので、いたずらし放題です。

複数代入抑制プロパティ登場

Eloquentの説明に書かれているセキュリティーリスクとは、前記の内容です。よほどのうっかり者でないと、起こさないような事故のようなものですが、実際の世の中でもうっかり事故が起きているように、いつ起きるか分かりません。

そこで、バージョン4から自由に複数代入ができないようになりました。プロパティで指定しないと、デフォルトのままでは複数代入ができません。

ユーザーによく考えてもらい、前記のようなリスクを含まないと確信できるフィールドのみ、プロパティで代入を許すように指定してもらうようになりました。しかし、こうした事情を知らなければ、注意のしようもありません。

初めてEloquentを使用する方は、複数代入にはこうしたリスクがあることを理解しておきましょう。