This is IT

技術、日常

【pg gem】execメソッドと、exec_paramsメソッドの違い

環境

WSL2 Debian 11.5

結論

  • exec:明示的に、SQL文の中にパラメーターを直接記述してメソッドを使う。
  • exec_params:明示的に、SQL文のパラメーターは、プレースホルダーを指定してメソッドを使う。

それぞれのコード例

execexec_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インジェクションの攻撃は防ぐことが可能です。

まとめ

参考サイト