RailsでのSQLインジェクションに対し考慮したいこと
この記事は ゲームエイト開発部 Advent Calendar 2023 の記事です。
RailsにおけるSQLインジェクションの防止
はじめに
弊社では開発のバックエンドにRailsを採用しています。
Railsではデフォルトにて脆弱性対策がされていますが、書き手側で考慮しないといけないこともあります。
今回はその中でもSQLインジェクションに絞って話をしていきます。
SQLインジェクションとは
SQLインジェクションは、攻撃者が不正なSQLコマンドをデータベースに注入し、セキュリティを侵害する攻撃手法です。一般的な攻撃例として、ウェブフォームからの攻撃が挙げられます。
例えば、ログインフォームに
' OR '1'='1
といったSQL文を注入することで、認証メカニズムを回避し、不正アクセスを行うことが可能です。
これはデータベースからの機密情報の漏洩やデータの不正改ざんにつながる可能性があります。
RailsにおけるデフォルトのSQLインジェクション対策
Railsは、Active Recordを通じてSQLインジェクション攻撃を防ぐためのメカニズムを提供しています。
Active Recordクエリメソッド
app/models/user.rb におけるActive Recordクエリの例:
class User < ApplicationRecord
# ...
def self.find_by_name(name)
where("name = ?", name)
end
end
このコードでは、whereメソッドがプレースホルダー(?)を使用しており、Railsが対応する値をエスケープします。
Named Parameter
名前付きパラメータの使用例:
class UsersController < ApplicationController
# ...
def show
@user = User.find_by(name: params[:name])
end
Railsはパラメータを自動的にサニタイズします。
RailsにおけるSQLインジェクション対策の不足点
Railsのデフォルト機能だけでは全てのSQLインジェクションリスクを防ぐことはできません。開発者は以下の点に注意する必要があります。
パラメータのサニタイズ:
直接SQLクエリにユーザー入力を含める際は、そのパラメータをサニタイズすることが重要です。Railsでは、プレースホルダを使用してパラメータを安全に組み込むことができます。
# 安全でない方法
name = params[:name]
User.where("name = '#{name}'")
# 安全な方法
name = params[:name]
User.where('name = ?', name)
ActiveRecordの利用:
RailsのActiveRecordを使用することで、SQLインジェクションのリスクを大幅に減らすことができます。ActiveRecordは、SQLクエリを自動的にエスケープし、安全な形式でデータベースに渡します。
# 安全な方法
name = params[:name]
User.where(name: name)
生のSQLクエリの使用を避ける:
可能な限り生のSQLクエリの使用を避け、ActiveRecordのメソッドを利用することを推奨します。どうしても生のSQLが必要な場合は、特に注意が必要です。
ユーザー入力の検証:
フォームやAPIを通じて受け取ったデータは、サーバー側で厳格に検証し、不正なデータがデータベースに渡されないようにする必要があります。
# 入力の検証
name = params[:name]
unless name.match?(/\A[a-zA-Z]+\z/)
# 不正な入力を処理
end
# 安全なクエリの実行
User.where(name: name)
セキュリティ更新の適用:
Rails自体や使用しているGemのセキュリティアップデートを定期的にチェックし、最新の状態を保つことが重要です。
おわりに
Railsでは脆弱性対策が実施されていますが、すべてのシナリオで完全な保護を提供するわけではありません。開発者は、アプリケーションのセキュリティを確保するために、常に自身のコードに対し疑念を持って確認できるようにしていきたいですね。
Discussion