はじめに
Rails Tutorialを進めていく中で気になったところを記事にして残してます。
記事に間違いがある場合は教えてください
用語解説
SQLインジェクションとは
SQLインジェクションは、Webアプリケーションのパラメータを操作してデータベースクエリに影響を与えることを目的とした攻撃手法です。SQLインジェクションは、認証をバイパスする目的でよく使われます。他にも、データを操作したり任意のデータを読み出したりする目的にも使われます。
つまり、ログインフォームや投稿フォームなどでSQLのデータを処理する際に、不正**(サーバーからみると正常)**なデータが実行されてしまうことです。
これによってログイン偽装やデータの抜き取りが起こります。
エスケープ処理とは
「'」「"」「NULL」「改行」などのSQL文において都合の悪い文字を使えなくすることです。
悪い例
不正に認証が通ってしまう
以下のコードでログイン処理をするとしましょう。
User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")
一見問題はなさそうに見えますが、もし以下のパラメータが入っていたとします。
params = { name: "' OR '1'='1",
password: "' OR '2'>'1" }
これを実行すると以下のようなSQL文が呼ばれることになります。
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
要約すると1=1
, 2>1
が成り立つ時usersから1人取り出してくださいということです。
ログインとしての機能を果たさなくなってしまいました。
原因
こうなってしまう原因はエスケープ処理をしていないからです。
対策
対策としてSQL文を書くときには直接文字列を代入するのではなく、必ずエスケープ処理をするようにしましょう。
エスケープ処理には以下の方法があります。
- 配列、ハッシュとして渡す。(モデルのインスタンスのみ)
- sanitize_sql()を使う。(それ以外)
対策1
モデルの場合はこちらが楽です。
# 配列
Model.where("login = ? AND password = ?", entered_user_name, entered_password).first
# もしくはハッシュ
Model.where(login: entered_user_name, password: entered_password).first
対策2
sanitize_sqlというメソッドでエスケープできます。
# 3つの例
sanitize_sql(["name=? and group_id=?", "foo'bar", 4])
sanitize_sql(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
sanitize_sql("name='foo''bar' and group_id='4'")
参照
Rails セキュリティガイド - Railsガイド
ActiveRecord::Sanitization::ClassMethods - Rails API