はじめに
こんにちは。アメリカに住みながら、独学でエンジニアを目指している Taira です。
現在セキュリティについて、徳丸本を使用しながら学習中ですが、そこで出てきた SQL インジェクションについてまとめてみようと思います。
SQL インジェクションとは?
SQL インジェクション(SQL Injection)は、ユーザーからの入力値がアプリケーションによって適切に検証・エスケープされずにそのまま SQL クエリとしてデータベースに渡されることで、悪意のある SQL 文が実行されてしまう脆弱性です。
攻撃者の目的
- ログインをバイパスする
- 本来見られない機密データを抽出する
- データの改ざん・削除を行う
- データベースサーバーに対して DoS 攻撃を仕掛ける
仕組みの概要
通常、アプリケーションでは以下のようにユーザーの入力をもとに SQL 文を生成して、データベースへ問い合わせを行います。
User.where("name = '#{params[:name]}'")
このとき、params[:name]
に "' OR '1'='1"
というような値を与えると、次のような SQL 文が生成されてしまいます:
SELECT * FROM users WHERE name = '' OR '1'='1'
このクエリは常に真となる条件を持つため、すべてのユーザー情報を取得してしまう危険性があります。これが SQL インジェクションの基本的な攻撃原理です。
このような攻撃は、データの漏洩、改ざん、アカウントのなりすまし、さらにはアプリケーション全体の制御権の奪取にまでつながる可能性があります。
安全な書き方(推奨)
Rails では、**バインドパラメータ(プレースホルダ)**を使用することで、SQL インジェクションを防ぐことができます。
User.where("name = ?", params[:name])
このように書くことで、params[:name]
の値は SQL 文に直接埋め込まれず、適切にエスケープ処理された上でクエリに渡されます。その結果、不正な入力が SQL 文の構造を壊すことができなくなり、安全に値を扱うことができます。
Active Record では他にも以下のような安全な記述が可能です:
User.find_by(name: params[:name])
これらの記法は内部でプレースホルダを使用しており、安全性が保たれています。
User.where("name = ?", params[:name])
まとめ
SQL インジェクションは、ユーザー入力を適切に扱わないことで発生する重大な脆弱性です。特に Web アプリケーションでは、入力値をそのまま SQL 文に組み込むのは非常に危険です。
- 危険な例:
where("name = '#{params[:name]}'")
- 安全な例:
where("name = ?", params[:name])
Rails のようなフレームワークを使う場合でも、常に「プレースホルダ」や「安全なメソッド」を使うことで、SQL インジェクションを防ぐことができます。