Railsのセキュリティ重視ルーティング完全ガイド!初心者でもわかる公開範囲・HTTPメソッド制限・CSRF対策
生徒
「Railsのルーティングって、セキュリティにも関係あるんですか?」
先生
「もちろん関係ありますよ!ルーティングの設計を間違えると、誰でもアクセスできたり、意図しない操作ができるようになってしまうこともあるんです。」
生徒
「それは怖いですね…。どうすれば安全なルーティングになりますか?」
先生
「それでは、セキュリティの観点からルーティングの注意点を学んでいきましょう!」
1. 公開範囲を限定して不要なルートを避ける
Railsのresourcesを使うと、自動的に7つのルート(index、show、new、edit、create、update、destroy)が作成されますが、本当に全部必要ですか?
たとえば「一覧表示」と「詳細だけで良い」場合、それ以外は使わせないように限定しましょう。
resources :products, only: [:index, :show]
onlyやexceptオプションを使ってルートを制限することで、外部からの不正アクセスを防ぐことができます。
2. HTTPメソッドの強制で安全性を高める
HTTPメソッドとは、Webページとサーバーのやりとりで使われる動作のことです。たとえば、GETはページの取得、POSTは新規登録、DELETEは削除など。
Railsでは、HTTPメソッドが一致しないとルートに到達しないため、意図しない操作を防げます。以下はGET専用のルートの例です。
get '/admin/dashboard', to: 'admin#dashboard'
これで「URLにアクセスしてページを見る」以外の動作はできません。重要な操作にはGETを使わないというのが基本です。
3. 重要操作にはPOST・PATCH・DELETEを使おう
「削除」や「更新」などの重要な処理は、URLからアクセスできないようにして、POSTやDELETEなどのフォームやボタンからだけアクセスできるようにします。
これは「誰かが悪意のあるURLを作って、間違って開いただけでデータが消える」という事故を防ぐためです。
<form action="/products/1" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="削除する">
</form>
このように_methodという隠しフィールドで、DELETEを指定できます。
4. CSRF攻撃って何?Railsではどう防ぐ?
CSRF(しーえすあーるえふ)は「クロスサイトリクエストフォージェリ」の略で、勝手に操作されてしまう攻撃のことです。
たとえば、ログイン中の管理者が悪意のあるページを開いてしまうと、自分の知らない間に削除ボタンを押されたことになるようなケースです。
RailsではデフォルトでCSRF対策が有効になっていて、フォームには「トークン」という安全の鍵を自動で埋め込んでいます。
<%= form_with model: @product do |form| %>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
form_withなどのヘルパーを使うことで、自動でCSRF対策が入るので安心です。
5. APIモードではCSRFが無効?注意点も解説
Railsを--apiモードで作成した場合、CSRF対策は無効になっています。APIでは主にトークンベースの認証を使うためです。
そのため、もしWebアプリとして使う場合は、protect_from_forgeryを明示的に有効化する必要があります。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
APIとして使うならCSRFは無効でも大丈夫ですが、Webフォームがある場合は有効化が必須です。
6. 管理画面や機密ページはIPやログインで制限しよう
ルーティングだけでは防げないアクセス制限もあります。たとえば/adminのような管理画面は、誰でもアクセスできるURLにしてはいけません。
通常はログイン機能やIPアドレス制限を併用して、セキュリティを高めます。Railsではルーティングにconstraintsを使って、IP制限をかけることも可能です。
constraints ->(req) { req.remote_ip == "192.168.0.1" } do
get '/admin', to: 'admin#dashboard'
end
これで特定のIPアドレスからしかアクセスできなくなります。