Ruby on rails 注册或邀请电子邮件验证,不含数据库

Ruby on rails 注册或邀请电子邮件验证,不含数据库,ruby-on-rails,ruby,database,encryption,encoding,Ruby On Rails,Ruby,Database,Encryption,Encoding,我想保持我的数据库中几乎没有过时的帐户,我正在考虑进行新的注册和邀请,将他们的数据作为加密或散列的url放入欢迎电子邮件中。访问url中的链接后,该信息将作为帐户添加到数据库中。 目前有什么东西可以做到这一点吗?关于以这种方式进行用户注册有任何参考、想法或警告吗? 谢谢 编辑: 我已经做了一个工作示例,url是127个字符 http://localhost/confirm?_=hBRCGVqie5PetQhjiagq9F6kmi7luVxpcpEYMWaxrtSHIPA3rF0Hufy6EgiH

我想保持我的数据库中几乎没有过时的帐户,我正在考虑进行新的注册和邀请,将他们的数据作为加密或散列的url放入欢迎电子邮件中。访问url中的链接后,该信息将作为帐户添加到数据库中。 目前有什么东西可以做到这一点吗?关于以这种方式进行用户注册有任何参考、想法或警告吗? 谢谢

编辑: 我已经做了一个工作示例,url是127个字符

http://localhost/confirm?_=hBRCGVqie5PetQhjiagq9F6kmi7luVxpcpEYMWaxrtSHIPA3rF0Hufy6EgiH%0A%2BL3t9dcgV9es9Zywkl4F1lcMyA%3D%3D%0A
显然,数据越多,url越大

def create
# Write k keys in params[:user] as v keys in to_encrypt, doing this saves LOTS of unnecessary chars
  @to_encrypt = Hash.new
  {:firstname => :fn,:lastname => :ln,:email => :el,:username => :un,:password => :pd}.each do |k,v|
    @to_encrypt[v] = params[:user][k]
  end

  encrypted_params = CGI::escape(Base64.encode64(encrypt(compress(Marshal.dump(@to_encrypt)), "secret")))
end

private

def aes(m,t,k)
  (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k)
  aes.update(t) << aes.final
end

def encrypt(text, key)
  aes(:encrypt, text, key)
end

def decrypt(text, key)
  aes(:decrypt, text, key)
end

# All attempts to compress returned a longer url (Bypassed by return)

def compress(string)
  return string
  z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
  o = z.deflate(string,Zlib::FINISH)
  z.close
  o
end

def decompress(string)
  return string
  z = Zlib::Inflate.new
  o = z.inflate(string)
  z.finish
  z.close
  o
end
def创建
#将params[:user]中的k密钥作为v密钥写入_encrypt,这样做可以节省大量不必要的字符
@to_encrypt=Hash.new
{:firstname=>:fn,:lastname=>:ln,:email=>:el,:username=>:un,:password=>:pd}。每个do | k,v|
@加密[v]=params[:user][k]
结束
encrypted_params=CGI::escape(Base64.encode64(encrypt(compress(Marshal.dump(@to_encrypt)),“secret”))
结束
私有的
def aes(m、t、k)
(aes=OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key=Digest::SHA256.Digest(k)
aes.更新(t)想法:

  • 对“cookie”使用真正的非对称密码,以防止机器人创建帐户。使用公钥加密“cookie”,使用私钥解码验证它。
    理由:如果仅使用base64或其他算法对cookie进行编码,则很容易对方案进行反向工程并自动创建帐户。这是不可取的,因为spambots。此外,如果帐户受密码保护,则密码必须出现在cookie中。任何有权访问注册链接的人都将不仅能够激活该帐户,而且能够计算出密码

  • 通过链接激活后需要重新输入密码。
    理由:根据网站的用途,您可能希望提高对信息欺骗的保护。激活后重新输入密码可防止被盗/伪造的激活链接

  • 验证激活链接时,请确保它创建的帐户尚未创建。

  • 如何防止两个用户同时创建同名帐户?
    可能的答案:使用电子邮件作为登录标识符,不需要唯一的帐户名

  • 首先验证电子邮件,然后继续创建帐户。
    理由:这将最大限度地减少您需要在cookie中发送的信息

      • 有些电子邮件客户端会在80个字母后中断URL。我怀疑你能把所有的信息都放进去

      • 一些浏览器对URL有限制,例如限制为2083个字符


      为什么不定期清理数据库(cron脚本)并删除24小时内未激活的所有帐户?

      我将尝试描述一种可能有效的设计

      先决条件:

      • 支持RSA和一些安全哈希函数H(如SHA-1)的密码库
      • 一对私钥和公钥
      设计:

      • 唯一用户标识符是电子邮件地址
      • 帐户具有关联的密码和可能的其他数据
      • 激活cookie尽可能小
      过程:

      • 要求用户提供电子邮件地址和密码。提交表单时,cookie计算为
        cookie=ENCRYPT(CONCAT(电子邮件),H(密码)),公钥)
      • 发送的电子邮件包含指向带有cookie的激活页面的链接,例如。
        http://example.org/activation?cookie=[cookie]
      • 激活页面位于
        http://example.org/activation
        对作为参数传递的cookie进行解密:
        data=SPLIT(解密(cookie,私钥),'.'))
      • 在同一激活页面中,要求用户输入密码(密码必须散列到与cookie中相同的值)以及创建帐户所需的任何其他信息
      • 提交激活页面后,将创建一个新帐户

      请指出我遗漏的任何内容或任何改进。我很乐意相应地更新答案。

      我以前也做过类似的事情。我只有两条建议给你

    • 添加密钥版本,以便在不中断未完成确认的情况下旋转密钥
    • 您需要一个时间戳或到期日,以便您可以在确认时设置一个时间限制。我们有一周的时间
    • 至于最短的URL,您可以通过以下更改做得更好

    • 使用流密码模式,如CFB,这样就不必填充到块大小
    • 当数据较大时,压缩明文会有所帮助。我有一个标志,只在压缩数据时使用压缩
    • 使用
      Base64.urlsafe\u encode64()

    • 你的解决方案有一些问题

      首先,您没有设置密码的IV。在我看来,这暴露了Ruby OpenSSL包装中的一个严重缺陷—在设置了
      密钥
      iv
      之前,它不应该让您执行加密或解密,但是它正在继续,并且使用了全零的iv。每次使用同一个IV基本上从一开始就消除了使用反馈模式的许多好处

      其次,更严重的是,你没有真实性检查。CBC模式的一个特性是,可以访问一条消息的攻击者可以修改该消息以创建第二条消息,其中第二条消息中的块具有完全由攻击者控制的内容,而代价是先前的块被完全篡改。(哦,注意循环流化床模式在这方面也是一个问题)

      在这种情况下,这意味着我可以
      ?ln=Last%20Name&fn=First%20Name&email=foo@bar.com&hmac=7fpsQba2GMepELxilVUEfwl3%2BN1MdCsg%2FZ59dDd63QE%3D