4

テキスト列がユーザー指定の文字列で始まる行を検索したいのですが、たとえばSELECT * FROM users WHERE name LIKE 'rob%'、「rob」は検証されていないユーザー入力です。ユーザーが「rob_」などの特殊なパターン文字を含む文字列を書き込むと、「robert42」と「rob_the_man」の両方に一致します。文字列が文字通り一致していることを確認する必要があります。どうすればよいですか? アプリケーションレベルでエスケープを処理する必要がありますか、それともより美しい方法ですか?

Goには PostgreSQL 9.1 とgo-pgsqlを使用しています。

4

4 に答える 4

8

_文字と%文字は、LIKEステートメントで文字通り一致するように引用符で囲む必要があり、それを回避する方法はありません。選択は、クライアント側またはサーバー側で行うことです(通常、SQL replace()を使用します。以下を参照してください)。また、一般的なケースで100%正しくするために、考慮すべきことがいくつかあります。

デフォルトでは、_または%の前に使用する引用文字は円記号(\)ですが、LIKE句の直後にあるESCAPE句を使用して変更できます。いずれの場合も、文字通り1文字として一致させるには、引用文字をパターン内で2回繰り返す必要があります。

例:john%node1 ^node2.uccp@の... WHERE field like 'john^%node1^^node2.uucp@%' ESCAPE '^'後に何かが続くと一致します。

バックスラッシュのデフォルトの選択には問題があります。standard_conforming_stringsがオフの場合、他の目的ですでに使用されています(PG 9.1ではデフォルトでオンになっていますが、以前のバージョンはまだ広く使用されているため、これは考慮事項です)。

また、LIKEワイルドカードの引用は、ユーザー入力インジェクションシナリオでクライアント側で行われる場合、ユーザー入力ですでに必要な通常の文字列引用に追加されます。

go-pgsqlの例を見ると、変数に$ Nスタイルのプレースホルダーが使用されていることがわかります...そこで、なんらかの一般的な方法で記述しようとします。ONまたはOFFの両方でstandard_conforming_stringsで動作し、サーバー側の置換を使用します。 [%_]、代替引用文字、引用文字の引用、およびSQLインジェクションの回避:

   db.Query("SELECT * from USERS where name like replace(replace(replace($1,'^','^^'),'%','^%'),'_','^_') ||'%' ESCAPE '^'",
     variable_user_input);
于 2012-04-14T16:37:08.713 に答える
2

私が知る限り、LIKE 演算子を使用する唯一の特殊文字はパーセントとアンダースコアであり、これらはバックスラッシュを使用して手動で簡単にエスケープできます。あまり美しくはありませんが、機能します。

SELECT * FROM users WHERE name LIKE
regexp_replace('rob', '(%|_)', '\\\1', 'g') || '%';

PostgreSQL にそのような関数が同梱されていないのは奇妙に思います。ユーザーに独自のパターンを作成してもらいたいのは誰ですか?

于 2012-04-14T16:34:41.680 に答える
-2

最良の答えは、ユーザー入力を sql に補間するべきではないということです。SQL をエスケープすることでさえ危険です。

go の db/sql ライブラリを使用する以下は、より安全な方法を示しています。Prepare および Exec 呼び出しを、go postgresql ライブラリの同等のものに置き換えます。

// The question mark tells the database server that we will provide
// the LIKE parameter later in the Exec call
sql := "SELECT * FROM users where name LIKE ?"
// no need to escape since this won't be interpolated into the sql string.
value := "%" + user_input
// prepare the completely safe sql string.
stmt, err := db.Prepare(sql)
// Now execute that sql with the values for every occurence of the question mark.
result, err := stmt.Exec(value)

これの利点は、実行するステートメントに sql が挿入されることを恐れずに、ユーザー入力を安全に使用できることです。また、複数のクエリに対して準備された sql を再利用する利点も得られます。これは、場合によってはより効率的です。

于 2012-04-16T21:47:20.030 に答える