Rails将普通旧字符串保存为SQlite作为BLOB?我快要疯了!

时间:2011-07-04 19:22:55

标签: ruby-on-rails sqlite

我不知道为什么会发生这种情况,但是Rails将字符串保存为SQLite作为BLOB。在我的应用程序中创建新用户之前,我在保存到数据库之前使用其纯字符串密码和MD5:

class User < ActiveRecord::Base
  before_create :encrypt_password

  def encrypt_password
    self.password = Digest::MD5.hexdigest(self.password)
  end
end

但是,密码字段每次进入SQLite都是一个疯狂的BLOB!我甚至可以告诉的唯一方法是将表导出到SQL时我可以看到该字段的真实性质:

INSERT INTO "users" VALUES (24, 'john.doe@example.com', X'3639366432396530393430613439353737343866653366633965666432326133');

到底是什么?所以现在当我尝试通过查找他们的电子邮件和MD5哈希密码来验证用户时,它每次都会失败。 REAL字符串显然与BLOB不匹配:

User.find_by_email_and_password('john.doe@example.com', Digest::MD5.hexdigest('password') => nil

我一生中从未使用BLOB,更不用说作为用户表的密码字段了。我的迁移明确将:string定义为数据类型。执行User.columns清楚地显示:

#<ActiveRecord::ConnectionAdapters::SQLiteColumn:0x00000105256ce0 @name="password", @sql_type="varchar(255)"

我一直在为这个应用程序工作一个月的大部分时间,直到昨晚我为User模型编写了一些测试时才从未见过这个问题。尝试对用户进行身份验证的测试每次都会失败,因此我开始在控制台中手动构建用户,并发现密码永远不会匹配,因此所有用户查找都将失败。

用于创建用户的Rails调试信息如下所示:

INSERT INTO "users" ("created_at", "email", "first_name", "last_login_at", "last_name", "login_count", "password", "role_id", "twitter", "updated_at", "uuid") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["created_at", Mon, 04 Jul 2011 18:50:58 UTC +00:00], ["email", "braulio_towne@runolfsson.name"], ["first_name", "Ebba"], ["last_login_at", nil], ["last_name", "Bayer"], ["login_count", nil], ["password", "5f4dcc3b5aa765d61d8327deb882cf99"], ["role_id", 2], ["twitter", nil], ["updated_at", Mon, 04 Jul 2011 18:50:58 UTC +00:00], ["uuid", "7ab57110-889c-012e-e207-482a140835c4"]]

其中,当我转换为常规SQL工作正常时,所以必须在某处发生其他事情。发生了什么事???!

  • sqlite3 3.6.12
  • rails 3.1.0.rc4
  • sqlite3-ruby 1.3.3

更新

它变得陌生和陌生......如果我在encrypt_password方法中对一些随机字符串进行硬编码,那么它会正确地进入数据库:

def encrypt_password
  self.password = 'foo'
end

我甚至可以将其硬编码为字符串'password'的实际MD5哈希值,并且可以正常工作:

def encrypt_password
  self.password = '5f4dcc3b5aa765d61d8327deb882cf99'
end

但如果我告诉它Digest::MD5.hexdigest('password')那么它就会以BLOB的形式出现。

现在,如果我将任何添加到摘要创建的字符串中,那么它就可以了!

def encrypt_password
  self.password = Digest::MD5.hexdigest(self.password) + ' '
end

到底是什么意思?所以现在我的训练是添加换行符,然后将其扼杀:

def encrypt_password
  self.password = (Digest::MD5.hexdigest(self.password) + "\n").chomp
end

我觉得我应该在某个地方的Rails打开一张票,但这真是太奇怪了,我不想永远笑出社区,甚至暗示这样的事情可能会发生!

2 个答案:

答案 0 :(得分:6)

原来这是一个编码问题。在此处查看门票:https://github.com/rails/rails/issues/1965

hexdigest返回ASCII字符串,但当您返回查询同一字段时,查询将作为UTF-8字符串运行。我假设一旦我手动将某些内容添加到字符串中,它就会在幕后转换为UTF-8,然后以UTF-8的形式正确保存到数据库中。这是修复:

def encrypt_password
  self.password = Digest::MD5.hexdigest(self.password).encode('UTF-8')
end

答案 1 :(得分:0)

直接使用MD5哈希对于安全性来说不是很好。有人使用这种方法打破了其他地方的电子邮件/密码数据库,他们可以轻松入侵您的网站。

考虑使用Bcrypt。以下摘自http://bcrypt-ruby.rubyforge.org

require 'bcrypt'

class User < ActiveRecord::Base
  # users.password_hash in the database is a :string
  include BCrypt

  def password
    @password ||= Password.new(password_hash)
  end

  def password=(new_password)
    @password = Password.create(new_password)
    self.password_hash = @password
  end

end

添加迁移以将'password'重命名为'password_hash'。

这也应该有解决你使用hexdigest方法的结果所带来的序列化问题的效果。