elasticsearch-ruby で外部入力から検索時の json 用文字列のエスケープ処理

Elasticsearch の Ruby クライアントである elasticsearch-ruby を使って、例えばウェブページの検索フォームでユーザーの外部入力から検索を行う場合、外部入力された文字列を適切にエスケープすることが必要なはずです。適当にぐぐって調べてみましたけど、NoSQL なデータベースに対するインジェクション攻撃のようなトピックは意外と情報が少ない。

スポンサーリンク

以下これまでに作業を行った Elasticsearch 関連のトピックです。新しいのが上。

Elasticsearch を Ruby から使う | EasyRamble
ElasticsearchにMySQLからデータ挿入、JDBC River Pluginのインストールと使い方 | EasyRamble
Elasticsearchのクエリとフィルターで簡単な検索を試す例 | EasyRamble
ElasticsearchのインストールとCSVからのデータ挿入 | EasyRamble

elasticsearch-ruby で検索を行う際の文字列エスケープ

なかなか情報が少ない中、以下の Google グループの elasticsearch-ruby に関するトピックがめちゃ役に立ちました。

[RUBY] : elasticsearch-ruby : Special characters not escaped by the library – Google グループ

“generally avoid the query_string query for user facing searches” だそうです。retire した tire の gem 作者の方がお返事されているので信頼性高い。ユーザーが検索するような場面では、query_string のクエリ使用は避けて、simple_query_string のほうが良いかもとのこと。

以下 elasticsearch のドキュメントによると、simple_query_string は例外を投げず、かつクエリの invalid な箇所を破棄すると説明があります。

Simple Query String Query
Query String Query

Ruby で JSON の文字列データのエスケープ

以上を踏まえて… Elasticsearch はクエリを json で組み立てるので、まずは json データのエスケープについて調べました。

NoSQLを使うなら知っておきたいセキュリティの話(2):「JSON文字列へのインジェクション」と「パラメータの追加」 (1/2) – @IT
JSONのエスケープ | yohgaki’s blog
JSON でのエスケープ処理 (JSONの値に”””, “\” を含める場合の処理)

json のエスケープについては、各言語で用意されている API を使うのが確実かと思います。Ruby の場合は、JSON#generate(object, state = nil) -> String か Object#to_json を利用できる。require “json” が必要です。

Encode a Ruby string to a JSON string – Stack Overflow

エスケープだらけでわけわかめな感じですが、elasticsearch の検索に渡してみます。

う〜ん、”name”:”\”\\\” \\\\ / \\n \\r \\t\”” と json 文字列データの中でさらに \” で囲われているのでこのままじゃいけません。string.to_json の先頭の \” と最後の \” を削除して渡す。

これで良いかな。

Elasticsearch の予約語のエスケープ

また、Elasticsearch に特有の予約語がいくつかありまして、それらもエスケープさせる必要があります。

Query String Query

+ – && || ! ( ) { } [ ] ^ ” ~ * ? : \ /
AND OR NOT

以上が elasticsearch の予約語となっているので、これらもクエリの json に渡す際にエスケープする。以下 Stack Overflow で見つけたメソッドが大変参考になる。

ruby on rails 3.2 – Symbols in query-string for elasticsearch – Stack Overflow

また、調査の過程で見つけた Java の package org.apache.lucene.queryparser.classic の escape メソッド実装も参考になります。Elasticsearch の バックエンドである Apache Lucene 用パッケージの API。public static String escape(String s) の箇所です。

lucene: queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParserBase.java Source File – doxygen documentation | Fossies Dox

package org.json.simple で json 用の escape メソッド実装も見つけました。以下の static void escape(String s, StringBuffer sb) の箇所。

JSONValue.java – json-simple – JSON.simple – A simple Java toolkit for JSON – Google Project Hosting

独自の sanitize メソッド

以上のメソッドを参考するとともに String#to_json を利用して、外部入力からの elasticsearch 検索用に、文字列を sanitize するメソッドを作成しました。require “json” が必要です。

利用する Rails コントローラーに上記メソッドを private メソッドで定義して、以下のように使う。

pry で動作テスト。以下の文字列を渡してみます。

” \ \b \f \n \r \t
+ – & | ! ( ) { } [ ] ^ ~ * ? : /
AND OR NOT

良い感じにエスケープされています。

記号で検索する人はほぼ皆無でしょうから、さらに安全を期するために、検索フォームからの外部入力で記号の類の文字を一切受け付けないようにしても良いかもです。

以下、アルファベット・数字、ハイフン、ひらがな、カタカナ、漢字、半角スペースと全角スペース以外の記号などの入力文字を全部取り除いてしまうメソッド。ハイフン(-), AND, OR, NOT はエスケープさせます。

以下 pry で試行。

こんな感じで記号は削除されてしまいますが、検索用途の目的であれば大して支障もないかと思います。ウェブページのフォームなどユーザーの外部入力から elasticsearch-ruby を使って検索を行う場合は、用途に応じていずれかのメソッドを使おうと思います。間違いや漏れなどありましたら、ぜひご指摘お願いいたします!

スポンサーリンク
全文検索システムを実装するには、ElasticSearch がおすすめです。
スポンサーリンク
 
Twitterを使っていますのでフォローお願いたします!ブログの更新情報もつぶやいてます^^
(英語学習用)

Leave Your Message!