Ruby on Rails security best practices based on the official Rails Security Guide. Covers CSRF, SQL injection, XSS, session security, credentials management, rate limiting, and Content Security Policy.
From eccnpx claudepluginhub tatematsu-k/ai-development-skills --plugin eccThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Based on: Rails Security Guide and Rails core team practices.
Rails のデフォルトで有効。
<!-- レイアウトに必須 -->
<%= csrf_meta_tags %>
Rules:
protect_from_forgery with: :null_session# CRITICAL: 絶対にやってはいけない
User.where("name = '#{params[:name]}'")
User.where("name = " + params[:name])
# SAFE: パラメータ化クエリ
User.where("name = ?", params[:name])
User.where(name: params[:name])
User.where("name = :name", name: params[:name])
# SAFE: LIKE クエリ
User.where("name LIKE ?", "%#{sanitize_sql_like(params[:query])}%")
Rails はテンプレートの出力を自動エスケープする。
<!-- 自動エスケープ(安全) -->
<%= @user.bio %>
<!-- html_safe は信頼できるデータのみ -->
<%= sanitize(@user.bio, tags: %w[p br strong em]) %>
<!-- DANGER: 絶対に使わない -->
<%= raw @user.bio %>
<%= @user.bio.html_safe %>
# config/initializers/content_security_policy.rb
Rails.application.configure do
config.content_security_policy do |policy|
policy.default_src :self
policy.font_src :self, "https://fonts.gstatic.com"
policy.img_src :self, :data, "https:"
policy.object_src :none
policy.script_src :self
policy.style_src :self, "https://fonts.googleapis.com"
policy.connect_src :self
end
# Nonce を使用(推奨)
config.content_security_policy_nonce_generator = ->(request) {
request.session.id.to_s
}
config.content_security_policy_nonce_directives = %w[script-src style-src]
end
# ログイン後に必ず reset_session(セッション固定攻撃防止)
def create
user = User.authenticate(params[:email], params[:password])
if user
reset_session
session[:user_id] = user.id
redirect_to root_path
end
end
Rules:
user_id のみ保存(複雑なオブジェクトは不可)# 暗号化クレデンシャルの編集
bin/rails credentials:edit
# 環境別クレデンシャル
bin/rails credentials:edit --environment production
# 使用
Rails.application.credentials.secret_api_key
Rails.application.credentials.dig(:aws, :access_key_id)
Rules:
config/master.key は絶対にコミットしない(.gitignore に含まれる)config.require_master_key = true で本番環境での起動を保証class SessionsController < ApplicationController
rate_limit to: 10, within: 3.minutes, only: :create
end
class PasswordsController < ApplicationController
rate_limit to: 5, within: 1.minute, only: :create,
with: -> { redirect_to login_path, alert: "Too many attempts" }
end
# 常に current_user でスコープ(IDOR防止)
@project = current_user.projects.find(params[:id])
# BAD: ID直接検索(他ユーザーのデータにアクセス可能)
@project = Project.find(params[:id])
# Rails 8: expect(推奨 — 構造不一致で 400)
def article_params
params.expect(article: [:title, :body, :status])
end
# permit! は絶対に使わない
params.permit! # DANGER: 全パラメータを許可
# BAD: 行境界(改行で回避可能)
validates :url, format: { with: /^https?:\/\// }
# GOOD: 文字列境界
validates :url, format: { with: /\Ahttps?:\/\// }
Ruby の ^/$ は行境界。\A/\z が文字列境界。
Rails がデフォルトで設定:
X-Frame-Options: SAMEORIGIN — クリックジャッキング防止X-Content-Type-Options: nosniff — MIME スニッフィング防止Referrer-Policy: strict-origin-when-cross-origin# HSTS の有効化
config.force_ssl = true
# Active Storage でのバリデーション
class User < ApplicationRecord
has_one_attached :avatar
validates :avatar, content_type: %w[image/png image/jpeg],
size: { less_than: 5.megabytes }
end
Rules:
# config/application.rb
# Rails 8 ではデフォルトで strict
config.active_record.store_full_class_name_in_sti_column = true
# Brakeman: 静的解析
gem install brakeman
brakeman
# bundler-audit: Gem の脆弱性チェック
gem install bundler-audit
bundler-audit check --update
# Rails 組み込み
bin/rails routes # 不要なルートの確認
sanitize でエスケープされているreset_session がログイン後に呼ばれているconfig.force_ssl = true が本番で有効master.key が .gitignore に含まれている\A/\z を使用しているSources: