環境
WSL2 Debian 11.5
結論
それぞれのコード例
exec
・exec_params
のそれぞれのコード例を示しておきます。
# execメソッドを使う場合 id = 1 name = "title1" connection = PG::Connection.new(DB_URI) result = connection.exec("UPDATE titles SET title = '#{name}' WHERE id = '#{id}'") # exec_paramsメソッドを使う場合 id = 1 name = "title1" connection = PG::Connection.new(DB_URI) result = connection.exec("UPDATE titles SET title = $1 WHERE id = $2", [name, id])
execメソッドでは変数を展開しつつ、SQL文に直接値を埋め込んでいるのに反し、
exec_paramsメソッドでは、プレースホルダー内に変数を格納しておき、パラメーターは$1
と$2
でプレースホルダー内の位置情報を指定しています。
一応どちらもプレースホルダーを利用する/しないで実装をすることが出来るのですが、exec_params
だと、SQLインジェクション対策として、自動でエスケープ処理を行ってくれるため、exec_params
を利用することが推奨されています。
SQLインジェクションとは?
SQLインジェクション(英: SQL injection)とは、アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。また、その攻撃を可能とする脆弱性のことである。 Wikipedia
Wikipediaの例をもとに、SQLインジェクション攻撃の例と、exec_params
が対策案となる理由をもう少し深堀していきましょう。
例えば、下記のようなSQLクエリがあるとします。
SELECT * from titles WHERE name = '#{name}'
そこで悪意を持った第三者が、このようなデータをフォームから送信をしたと仮定します。
name: t' OR 't'='t
この値が渡された場合、SQLクエリは次のようになります。
SELECT * from titles WHERE name = 't' OR 't'='t'
このクエリでは、本来は入力したname
に一致するレコードのみを出力したいはずが、OR 't' = 't'
の条件により、必ず値がtrueになってしまうため、全てのレコードが出力されてしまいます。
exec_paramsがSQLインジェクションの対策となる理由
先ほどのexec_params
メソッドをもう一度示します。
# exec_paramsメソッドを使う場合 id = 1 name = "title1" connection = PG::Connection.new(DB_URI) result = connection.exec("UPDATE titles SET title = $1 WHERE id = $2", [name, id])
クエリ文の処理を行う際、初めに解析されるのは、
UPDATE titles SET title = $1 WHERE id = $2
の部分のみになります。
その後、$1
、$2
の位置情報をもとに、[name($1), id($2)]
の値が引き渡されることとなります。
exec_params
を利用するメリットとして、この値の引き渡しの際に、自動でエスケープ処理を行ってくれることがあります。
例えば、クォーテーションやバックスラッシュなどをエスケープをしてから、値を渡すため、先ほどのSQLインジェクションの攻撃は防ぐことが可能です。
まとめ
参考サイト