ユユユユユ

webエンジニアです

phoenix がマスアサインメントを防御するやりかた

コントローラで受け取ったユーザー入力のパラメータを、コンテクストを通してそのままレポジトリに渡すコードはわりと普通に書くものらしい。チュートリアルでも、ビューヘルパーが組み立てたこんな強烈なパラメータをほとんど検証せずにデータベースの近くまで持っていく書きかたがされていた。

%{
  "cart" => %{
    "items" => %{
      "0" => %{
        "id" => "#{cart_item.id}",
        "quantity" => "42"
      },
      "1" => %{...}
    }
  }
}

Rails の感覚だと、コントローラでストロングパラメータを使って許可するキーのみ通すようによく注意しないといけないとおもうところ。いったいどう安全を保証することができるのだろう、というのがこの週末の関心だった。どのような脆弱性も、注意深くあるだけでは防げないから繰り返してしまうわけで、防止機構としてどんなメカニズムがあるのかを知りたかった。

Ecto.Changeset.cast/4 がその役割を一手に引き受けているらしい。データスキーマの定義に則ってパラメータをフィルタしているから、まあストロングパラメータの概念と近い。 Rails はコントローラにそれを実装して、 Phoenix は Ecto のモジュールにそれを委ねている。逆にいうと、それを実装しているのが Ecto であるから、コントローラではたいした検証もする必要はないしすべきでもない、ということなのかもしれない。

ナイーブな実装であれば、登録できる情報はすべて更新もできる、というようになる。登録時に入力させたい項目と更新時に入力させたい項目が異なるのであれば、同じコンテクストのなかでもスキーマモジュールの changeset/2creation_changeset/2update_changeset/2 とかにわけて使い分けられる。そもそも登録のコンテクストと閲覧のコンテクストがわかれているのであれば、フィールドのリストはスキーマモジュールの定義ごとわかれているはずだから、それぞれ適当にリストを定義するような格好か。

Ecto.Schema がコンテクストごとに必要なフィールドのスコープを選ばせるのは単に物理データと論理データを切り離して select * を抑制するためとおもっていたけれど、それがそのまま入力のチェックにも適用できるというのは、なるほどなあ。いいアンラーニングができた気がする。