Techioz Blog

SQLite3 および Rack Basic 認証ではユーザー名を実行パラメータとして使用できません

概要

次のコード スニペットを考えてみましょう。

require "sqlite3"

db = SQLite3::Database.new "my.db"
p db.execute("select * from user where name = ?", ["my_user_name"])

これにより、ユーザー名が「my username」であるエントリがデータベースに出力されます。これは意図したとおりに機能します。

ここで、次のコード スニペット (config.ru) について考えてみましょう。

require "rack/auth/basic"
require "sqlite3"

use Rack::Auth::Basic, "my authentication" do |username, password|
  p username
  p db.execute("select * from user where name = ?", [username])
end

run do |env|
  [200, {}, []]
end

このサーバーにリクエストを送信すると、次のようになります。

curl -X POST -v -ujomy:abc123 localhost:9292 -d ''

(ラックアップを使用して) サーバーを起動したターミナルに次の出力が表示されます。

"my_user_name"
[]

そのため、SQL クエリから結果が返されません。

この行を db.execute ステートメントの前に挿入すると、次のようになります。

username = "my_user_name"

これで実際に結果が返されました。

ユーザー名を username.to_s に置き換えても機能しません。

ということは、Rack が提供するユーザー名変数の使用は sqlite では機能しないのでしょうか? username.class が String であるにもかかわらず、これはどのようにして可能でしょうか? username.size も “my_user_name”.size と等しくなります。

解決策

これはエンコードの問題です。このブロックでは、ユーザー名とパスワードが「ASCII-8BIT」としてエンコードされます。

use Rack::Auth::Basic, "my authentication" do |username, password|
  p username
  p db.execute("select * from user where name = ?", [username])
end

これを解決するには、次のように String#force_encoding を使用してエンコーディングを変更します。

use Rack::Auth::Basic, "my authentication" do |username, password|
  p username
  p db.execute("select * from user where name = ?", [username.force_encoding('UTF-8')])
end