Skip to content

Viewの基本: form_with を使って安全なフォームを作成する方法

はじめに

Webアプリケーションにおいて、ユーザーからのデータを受け取るためのHTMLフォームは不可欠な要素です。Ruby on Railsでは、フォームを簡単かつ安全に作成するための強力なヘルパーメソッドform_withが提供されています。

この記事では、form_withの基本的な使い方から、その裏側で何が行われているのか、そしてなぜそれが「安全」なのかについて詳しく解説します。form_forform_tagに慣れている方も、モダンなRails開発の標準であるform_withへの理解を深めていきましょう。

form_withとは?

form_withは、Rails 5.1から導入された統一的なフォームヘルパーです。それ以前に存在したform_for(モデルオブジェクトに紐づくフォーム用)とform_tag(特定のモデルに紐づかないフォーム用)の機能を統合し、よりシンプルで柔軟な記述が可能になりました。

1. モデルに紐づくフォームの作成 (modelオプション)

最も一般的な使い方は、Active Recordのモデルオブジェクトと連携させる方法です。新規作成(new)と編集(edit)で同じフォームテンプレートを共有できるのが大きなメリットです。

コントローラ

まず、コントローラでフォームが使用するオブジェクトを用意します。

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def new
    @article = Article.new # 新規作成用の空のオブジェクト
  end

  def edit
    @article = Article.find(params[:id]) # 編集対象のオブジェクト
  end

  # ... create, updateアクションなど
end

ビュー (_form.html.erb)

次に、ビューでform_withを使ってフォームを描画します。

erb
<%# app/views/articles/_form.html.erb %>

<%= form_with(model: @article) do |form| %>
  <div>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :content %><br>
    <%= form.text_area :content %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

このフォームはneweditの両方のアクションからパーシャルとして呼び出せます。

form_withの賢い挙動

ここでform_withは、渡された@articleオブジェクトの状態を自動で判別します。

  • @articleが新規オブジェクトの場合 (@article.persisted?false)

    • フォームのaction属性は/articlesになります。
    • HTTPメソッドはPOSTになります。
    • submitボタンのテキストは "Create Article" になります。
  • @articleがデータベースに保存済みのオブジェクトの場合 (@article.persisted?true)

    • フォームのaction属性は/articles/1のような具体的なパスになります。
    • HTTPメソッドはPATCHになります。(HTMLの<form>タグにはmethod="post"と出力されますが、内部に<input type="hidden" name="_method" value="patch">が生成され、Railsがこれを解釈します)
    • submitボタンのテキストは "Update Article" になります。

このように、オブジェクトの状態に応じて適切なリクエスト先とメソッドを自動で設定してくれるため、開発者はロジックを共通化できるのです。

2. モデルに紐づかないフォームの作成 (urlオプション)

検索フォームのように、特定のモデルオブジェクトと直接関連しないフォームを作成する場合は、urlオプションで送信先のパスを明示的に指定します。

erb
<%# 検索フォームの例 %>
<%= form_with(url: search_path, method: :get) do |form| %>
  <%= form.label :query, "Search for:" %>
  <%= form.text_field :query %>
  <%= form.submit "Search" %>
<% end %>
  • url: search_path: フォームの送信先をsearch_pathrails routesで確認できる名前付きルート)に設定します。
  • method: :get: 検索なので、HTTPメソッドをGETに指定しています。これにより、検索クエリがURLのクエリパラメータ(例: /search?query=Rails)として送信されます。

3. なぜform_withは安全なのか? - CSRF対策

form_with(およびRailsのフォームヘルパー全般)が生成するHTMLをよく見ると、見慣れないhiddenタイプのinputタグが2つ含まれていることに気づきます。

html
<form action="/articles" method="post">
  <!-- ... form fields ... -->

  <!-- 1. UTF-8エンコーディング強制 -->
  <input type="hidden" name="utf8" value="✓">

  <!-- 2. CSRFトークン -->
  <input type="hidden" name="authenticity_token" value="GENERATED_TOKEN">
</form>

このうち、セキュリティ上非常に重要なのが**authenticity_tokenです。これはCSRF(クロスサイト・リクエスト・フォージェリ)**という種類の攻撃を防ぐためのものです。

CSRF攻撃とは?

  1. 悪意のある攻撃者が、罠サイトに「あなたのサイトのデータを削除するリクエスト」を送信するリンクやフォームを仕掛ける。
  2. あなたのサイトにログイン中のユーザーが、その罠サイトを訪れてリンクをクリックしてしまう。
  3. ユーザーのブラウザは、ログイン情報(Cookie)を保持したまま、あなたのサイトに意図しないリクエストを送信してしまう。
  4. あなたのサイトは正規のユーザーからのリクエストだと誤認し、データが不正に操作されてしまう。

CSRFトークンによる防御

Railsは、form_withでフォームを生成する際に、セッションごとにユニークなauthenticity_tokenを埋め込みます。そして、POST, PATCH, DELETEなどのデータ変更を伴うリクエストを受け取った際に、送信されてきたトークンがサーバー側で保持している正しいトークンと一致するかを検証します。

悪意のあるサイトは、この正しいトークンを知ることができないため、偽のリクエストを送信してもRails側でブロックされます。form_withを使うだけで、この重要なセキュリティ対策が自動的に施されるのです。

まとめ

form_withは、Railsにおけるフォーム作成の標準的な方法です。

  • model: @object を使えば、新規作成と編集でロジックを共通化できる。
  • url: path を使えば、モデルに依存しないフォームも簡単に作れる。
  • オブジェクトの状態を判別し、適切なactionmethodを自動で設定してくれる。
  • CSRF対策が自動的に組み込まれており、安全なフォームを構築できる。

フォームを作成する際は、手で<form>タグを書くのではなく、常にform_withを利用することを心がけましょう。これにより、コードの可読性が向上し、アプリケーションのセキュリティも確保されます。

AI が自動生成した技術記事をまとめたテックブログ