ホーム DoRuby rails MySQLの文字列エスケープ

rails MySQLの文字列エスケープ

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

rails MySQLでの文字列エスケープ

 この記事について

今回はセキュリティ系のネタです。

Ruby on RailsではActiveRecord内部で文字列のエスケープを行うため、

SQLインジェクション対策を気にする場面が少ないかもしれません。

しかし、希にSQLのすべてや一部を生で記述する必要があったりして

文字列のエスケープに気を遣わなければならないこともあります。

そのときになって慌てないためにも、

今一度MySQLでの文字列エスケープについて考えてみたいと思います。

 エスケープ・クォート例

まずはMySQLでの文字列のエスケープ/クォートでの囲み方の一部について例をあげます。

mysql> select 'string \'' AS `name```;
+----------+
| name`    |
+----------+
| string ' |
+----------+
1 row in set (0.00 sec)

上記のSQLでは文字列リテラル「string’」を出力しています。

リテラルはシングルクォート「’」 またはダブルクォート「”」で囲み、

文字列中に含まれるクォートやバックスラッシュ「\」の前に

バックスラッシュ「\」をつけてエスケープします。

AS句でカラム名の別名をつけますが、この別名はバッククォート「`」で囲みます。

別名中にバッククォートが含まれる場合はバックスラッシュ「\」ではなく、

バッククォート「`」をふたつ続けてかきます。

mysql> SELECT `name` FROM `some_table`;
<結果省略>

上記のSQLではテーブル some_table からすべてのレコードのnameカラムを出力しています。

カラム名・テーブル名ともにバッククォート「`」で囲みます。

文字中にバッククォートが含まれる場合はバッククォート「`」をふたつ続けてかきます。

なおSELECT句の「`name`」は「’name’」としてはいけません。

「’name’」とした場合リテラルとみなされ、

レコード数だけ文字列「name」が出力されてしまいます。

mysql> SELECT `name` FROM `some_table` WHERE `name` LIKE '\%abc\_%';
<結果省略>

上記のSQLではテーブル some_tableからnameカラムが「%abc_」で前方一致するレコードのnameカラムを出力しています。

LIKEの後ろにくる「’\%abc\_%’」は文字列リテラルですが、

パーセント記号「%」やアンダースコア「_」はそのまま書くとワイルドカードとして機能します。

そのためパーセント記号「%」やアンダースコア「_」自体をヒットさせたい場合は

それぞれの文字の前にバックスラッシュ「\」をつけてエスケープします。

これを忘れると強制的に前方一致させるつもりでも、

入力値によっては部分一致検索が可能となってしまいます。

mysql> SELECT `name` FROM `some_table` ORDER BY `name` ASC;
<結果省略>

上記のSQLではテーブル some_tableからすべてのレコードのnameカラムをnameカラムの昇順で出力しています。

ORDER BY句でもカラム名はバッククォート「`」で囲むパターンです。

カラム名に続く ASC or DESCについては定数のようなもので、エスケープやクォーティングは行いません。

ASC or DESCに文字列を入力する場合は入力する文字列がASCかDESCのいずれかのみを入力させるよう注意する必要があります。

 まとめ

以上複雑なようですが、エスケープ・クォートの方法は以下4パターンになると思います。

railsでの実装例とともにまとめてみます。

文字列リテラルパターン

エスケープ・クォート方法

シングルクォート「’」 またはダブルクォート「”」で囲み、

文字列中に含まれるクォートやバックスラッシュ「\」の前に

バックスラッシュ「\」をつけてエスケープする。

railsでの実装例
str = "test'"
puts ActiveRecord::Base.connection.quote(str) 
#「'test\''」と出力

LIKE文字列リテラルパターン

エスケープ・クォート方法

文字列リテラルパターンの方法に加えて

パーセント記号「%」やアンダースコア「_」の前に

バックスラッシュ「\」をつけてエスケープする。

railsでの実装例
str = "%_test"
puts "'%s'" % ActiveRecord::Base.connection.quote_string(str).gsub(/([%_])/){"\\" + $1}
#「'\%\_test'」と出力

カラム・テーブル名パターン

エスケープ・クォート方法

カラム名・テーブル名ともにバッククォート「`」で囲みます。

別名中にバッククォートが含まれる場合はバッククォート「`」をふたつ続けてかきます。

railsでの実装例
str = "test`"
#rails3.1以上
puts ActiveRecord::Base.connection.quote_column_name(str)
#「`test```」と出力

#rails3.0
puts ActiveRecord::Base.connection.quote_column_name(str.gsub("`", "``"))
#「`test```」と出力

定数パターン(ASC DESCなど)

エスケープ・クォート方法

クォートで囲まない。入力する場合はホワイトリスト方式で厳密にチェックする。

注意が疎かになりがちなのが

カラム・テーブル名パターン

です。

まずバッククォート「`」で囲むのを忘れているケースをよくみます。

また、囲んでいてもrails3.0ではquote_column_nameする前に

文字列中のバッククォート「`」を自前で「“」に変換しなければいけません。

記事を共有

最近人気な記事