2009-08-21 12 views
31

IN句にSQLite within Pythonのパラメータ置換を使用しようとしています。SQLite "IN"句のパラメータ置換

import sqlite3 

c = sqlite3.connect(":memory:") 
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') 

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): 
    c.execute('INSERT INTO distro (name) VALUES (?)', [ name ]) 

desired_ids = ["1", "2", "5", "47"] 
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)),()) 
for result in result_set: 
    print result 

それが出て出力します:

(1, u'Ubuntu') (2, u'Fedora') (5, u'SuSE')

ドキュメントは、「[あ]なたは、そうするために、Pythonの文字列操作を使用してクエリを組み立てるべきではないと述べているようにここで示して完全に実行されている例がありますあなたのプログラムはSQLインジェクション攻撃に対して脆弱になります。「パラメータ置換を使用したいと思っています。

私は試してみてください。

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ]) 

私は空の結果セットを取得し、私がしようとすると:

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ]) 

私が手:

InterfaceError: Error binding parameter 0 - probably unsupported type.

私はどんな答えていることを願っていますがこの単純化された問題を解決するには、実行したい実際のクエリが二重にネストされたサブクエリにあることを指摘したいと思います。ウィットには:

UPDATE dir_x_user SET user_revision = user_attempted_revision 
WHERE user_id IN 
    (SELECT user_id FROM 
     (SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN 
      ("Argl883", "Manf496", "Mook657") GROUP BY user_id 
     ) 
    ) 
+0

すべての回答ありがとうございます。私が最後に私が代用しているすべてのパラメータに疑問符が必要であることがわかったとき、それは非常に意味がありました。 –

答えて

47

あなたは?秒の正しい数が必要ですが、それは、SQLインジェクションのリスクをもたらすことはありません:いいえあります

query = 'SELECT * FROM distro WHERE id IN (%s)' % ','.join('?' for i in desired_ids) 
c.execute(query, desired_ids) 

>>> result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % 
          ','.join('?'*len(desired_ids)), desired_ids) 
>>> print result_set.fetchall() 
[(1, u'Ubuntu'), (2, u'Fedora'), (5, u'SuSE')] 
+0

プレースホルダリストの文字列を生成する "ベスト"ソリューションの場合は+1 –

+0

代わりに名前付きパラメータを使用する簡単な方法はありますか? ':id1:id2:id3'などのようなものです。これは、他のいくつかの名前付きパラメータを持つより大きなクエリのコンテキストでこれを使用しています。 – User

+2

私はこれに数年後に着きますが、名前付きのパラメータも必要でした。私はちょうどこれをしました: 'query *" SELECT * FROM my_table WHERE my_param =:my_param AND id IN({}) "フォーマット( '、')範囲(len(desired_ids))))); params = {'my_param': 'foo'}; params.update({str(i):iのid、enumerateのid(desired_ids)}); result = cursor.execute(query、params) ' sqlite3モジュールは、文字列置換パラメータとして':0'、 ':1'、':2'のようなものに完全に満足しています。 (スタックオーバフローは実際にはコメントの中でコードの書式を殺します;ごめんなさい、読みにくいです) – geekofalltrades

10

アップデート:この作品:

import sqlite3 

c = sqlite3.connect(":memory:") 
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') 

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): 
    c.execute('INSERT INTO distro (name) VALUES (?)', (name,)) 

desired_ids = ["1", "2", "5", "47"] 
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % ("?," * len(desired_ids))[:-1], desired_ids) 
for result in result_set: 
    print result 

問題は、あなたが1を持っている必要があることでしたか?入力リストの各要素に対して

ステートメント("?," * len(desired_ids))[:-1]は、 "?"の繰り返し文字列を作成し、最後のカンマを切り捨てます。 desired_idsには各要素の疑問符が1つあります。

+0

それは素晴らしい説明でした。ありがとうございました。 –

2

私はいつもこのような何かをやって終わりますあなたがdesired_idsからの文字列を直接クエリに入れているわけではないからです。

+0

IN句で使用する値は、実際には別のシステムからエクスポートされたファイルに由来します。私は注入の危険性が非常に少ないと期待していますが、ボビーテーブルがいつ登場するかは決して分かりません。 –

+0

あなたがプログラムに入れているのは疑問符の束だけなので、injectonのリスクは0です。仮想的な攻撃者が行うことができるのは、疑問符の数を制御することだけです。攻撃ベクトルではありません。実際に外部から供給されるデータは?パラメータの受け渡しメカニズムを通常どおりに実行できます。 –

+0

を参照してください。はい、あなたは正しいです。 –

0

sqliteがsqlリクエストの長さに問題がある場合は、疑問符の無限の数が何かをうねらせる方法になる可能性があります。

18

http://www.sqlite.org/limits.html(項目9)によると、SQLiteではクエリに999個以上のパラメータを扱うことができないため、ここでの解決策(必要なプレースホルダのリストを生成)は失敗します。あなたはINを探しています。そうであれば、リストを分割して、その部分をループして、結果を自分自身で結合する必要があります。

IN句に何千もの項目が必要ない場合は、Alexのソリューションがこれを行う方法です(Djangoのやり方と思われる)。

+0

お役立ち情報ありがとう。私は自分のコードを改訂する必要があるかもしれません。 –