😎

SQLインジェクション対策について改め考えてみた

2022/12/19に公開

こんにちは! バックエンドエンジニアとして株式会社iCAREで働いている山岸
と申します。

こちらはiCARE Dev Advent Calendar 2022 第2レーン 19日目の記事です。

きっかけ

業務中にRails6.1でorderとpluckの変数または属性名以外の文字列(たとえばSQL関数など)が含まれていた場合、例外が発生している箇所の対応をしたのをきっかけとしてめエンジニアとして再度考えてみようと思ったためです。
こちらの例外は、SQLインジェクションを防ぐためとされております。
対応策としましては、SQLインジェクションの危険性がないかをきちんと確認した上で以下のようにArel.sql()でラップしてあげる事です。

# NG
Hoge.order("lpad(hoges.number, 10, '0')")

# OK 
Hoge.order(Arel.sql("lpad(hoges.number, 10, '0')"))

SQLインジェクション対策について

「SQLインジェクション」は、Webアプリケーションの脆弱性を利用してデータベースを不正に操作する攻撃のことを指します。

Hoge.where("number = '#{params[:number]}'")

例えば、このような数をユーザーが入力し検索できるコードがあったとします。
もし悪意のあるユーザーが' OR 1) --を入力したら次のようなSQLが発行されます。

SELECT * FROM hoges WHERE (number = '' OR 1) --')

この結果、WHERE number = '' OR 1かつ2つのダッシュ「--」が末尾に置かれると、以後に追加されるクエリがすべてコメントと見なされてしまいhogesテーブルの情報全てを取得することが出来てしまいます。
情報に個人情報が含まれていた場合、個人情報漏洩となり大変な事になってしまいます。

Railsには、「'」「"」「NULL」「改行」のような特殊文字をエスケープしてくれるので、
位置指定ハンドラやハッシュを用いてサニタイズすると良いです。

# 位置指定ハンドラ
Hoge.where("number = ?", entered_number)

# ハッシュ
Hoge.where(number: entered_number)

また、引数に生のSQL文字列を使用するときは、sanitize_sql_arrayやsanitize_sql_for_orderといったsanitize_sqlメソッドを使用しましょう。他にもsanitize_sqlメソッドはありますので確認して頂けたらと思います。

Hoge.where(sanitize_sql_array(["name=?", "hoge'hoge"]))
# => "name='hoge''hoge'"

Hoge.where(sanitize_sql_for_order([Arel.sql("field(number, ?)"), [1,3,2]])
# => "field(number, 1,3,2)"

最後に

少しでもこの記事を読みSQLインジェクション対策について知見を深めて頂けましたら幸いです。

参考サイト

https://qiita.com/jnchito/items/5f2f00c93c0ba68e4d31

https://railsguides.jp/security.html#sqlインジェクション

https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html

https://zenn.dev/igaiga/books/rails-practice-note/viewer/ar_sql_injection

Discussion