その他
    ホーム 技術発信 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する前に

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

    記事を共有