2011-10-27 6 views
2

ここで何が起こっているのか説明できますか? SQLステートメント文字列のプレースホールディング構文が期待通りに機能しないようです(または、違う方法で言えば、少なくとも驚きの原則に違反します)。実行時に予期しない置換/エスケープがvar2:それが動作SQLプレースホルダなしこのrubyデータベースクエリでSQLプレースホルダが予期せず置換されました

ruby-1.9.2-p290 :001 > puts RUBY_VERSION 
1.9.2 
=> nil 

ruby-1.9.2-p290 :002 > require 'ipaddr' 
=> true 

ruby-1.9.2-p290 :003 > require 'sqlite3' 
=> true 

ruby-1.9.2-p290 :004 > var1 = Addrinfo.ip("1.2.3.4") 
=> #<Addrinfo: 1.2.3.4> 

ruby-1.9.2-p290 :005 > var2 = var1.ip_address 
=> "1.2.3.4" 

ruby-1.9.2-p290 :006 > var3 = "1.2.3.4" 
=> "1.2.3.4" 

ruby-1.9.2-p290 :007 > var2 == var3 
=> true 

ruby-1.9.2-p290 :008 > var2 === var3 
=> true 

ruby-1.9.2-p290 :009 > var2.eql?(var3) 
=> true 

ruby-1.9.2-p290 :010 > db = SQLite3::Database.open("test.db") 
=> #<SQLite3::Database:0x00000100bcfce0> 

ruby-1.9.2-p290 :011 > db.execute("SELECT * FROM devices WHERE deviceaddr=?", var2) 
=> [] 

ruby-1.9.2-p290 :011 > db.execute("SELECT * FROM devices WHERE deviceaddr=?", var2.to_s) 
=> [] 

ruby-1.9.2-p290 :012 > db.execute("SELECT * FROM devices WHERE deviceaddr=?", var3) 
=> [["TEST_DEVICE", "1.2.3.4"]] 

(ただし、SQLインジェクションにDBを公開!):

ruby-1.9.2-p290 :013 > db.execute("SELECT * FROM devices WHERE deviceaddr='#{var2}'") 
=> [["TEST_DEVICE", "1.2.3.4"]] 

だから、この作品を作るための安全な方法は何ですか?

答えて

4

TL; DR:SQLiteはUTFを使用します。 Addrinfoの8ビットASCII出力を変換します。

一つの「安全な」方法はそう、Addrinfoからの出力にforce_encoding("UTF-8")を使用することです:

> var1.ip_address.encoding 
=> #<Encoding:ASCII-8BIT> 
> var3.encoding 
=> #<Encoding:UTF-8> 
> db.execute("SELECT * FROM foo WHERE ip=?", var2.force_encoding("UTF-8")) 
=> [["1.2.3.4"]]  
+1

私は2行の長さのコードサンプルを除いている答えがTLを必要とする方法を好む; DR :) – mikej

+0

@ mikejコードアレルギー、またはより良い方法を知っている人のために:)(そして私はいくつか編集しました。) –

+0

そうです。私が追加しなければならないことのほんの1つは、おそらく正しいエンコーディングを判断する最良の方法は、db自体からです: 'enc = db.encoding'し、次にvar1.ip_address.encode(enc)またはvar2.encode(enc) –