代码之家  ›  专栏  ›  技术社区  ›  Blaine

无数据库的注册或邀请电子邮件验证

  •  16
  • Blaine  · 技术社区  · 16 年前

    我想保持我的数据库中没有过时的账户,我正在考虑让新的注册和邀请将他们的数据作为加密或散列的网址放入欢迎电子邮件中。一旦访问了url中的链接,信息就会作为帐户添加到数据库中。

    编辑:

    http://localhost/confirm?_=hBRCGVqie5PetQhjiagq9F6kmi7luVxpcpEYMWaxrtSHIPA3rF0Hufy6EgiH%0A%2BL3t9dcgV9es9Zywkl4F1lcMyA%3D%3D%0A
    

    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
    
    5 回复  |  直到 16 年前
        1
  •  6
  •   Filip Navara    16 年前

    思想:

    • 使用真正的非对称密码作为“cookie”,以防止机器人创建帐户。使用公钥加密“cookie”,用私钥解码进行验证。
      理论基础: 如果只使用base64或其他算法对cookie进行编码,则很容易对方案进行逆向工程并自动创建帐户。这是不可取的,因为存在垃圾邮件机器人。此外,如果帐户受密码保护,则密码必须显示在cookie中。任何有权访问注册链接的人不仅可以激活帐户,还可以找出密码。

    • 通过链接激活后需要重新输入密码。
      理论基础: 根据网站的目的,您可能需要改进对信息欺骗的保护。激活后重新输入密码可防止激活链接被盗/被欺骗。

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

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

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

        2
  •  4
  •   Georg Schölly Crazy Developer    16 年前
    • 有些电子邮件客户端会在80个字母后中断URL。我怀疑你能把所有的信息都放进去。

    • Internet Explorer 8 例如限制为2083个字符。

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

        3
  •  3
  •   ZZ Coder    16 年前

    我以前也做过差不多的事。我只有两个建议,

    1. 添加密钥版本,这样您就可以在不破坏未完成确认的情况下旋转密钥。
    2. 您需要一个时间戳或过期时间,以便在需要时设置确认的时间限制。我们允许一周的时间。

    对于最短的URL,您可以通过以下更改做得更好,

    1. 使用像CFB这样的流密码模式,这样你就不必填充到块大小。
    2. 当数据很大时,压缩明文会有所帮助。我有一个标志,只在压缩数据时使用压缩。
    3. 使用 Base64.urlsafe_encode64() 所以你不必对它进行URL编码。
        4
  •  3
  •   caf    16 年前

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

    首先,你没有设置密码的IV。在我看来,这暴露了Ruby OpenSSL包装器中的一个严重错误——在两者都执行之前,它不应该让你执行加密或解密 key iv

    其次,更严重的是,你没有进行真实性检查。CBC模式的一个特性是,有权访问一条消息的攻击者可以修改它以创建第二条消息,其中第二条信息中的一个块完全由攻击者控制内容,代价是前一个块被完全混淆。(哦,请注意,CFB模式在这方面同样是一个问题)。

    在这种情况下,这意味着我可以请求一个姓氏为AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA的帐户和我自己的电子邮件地址来接收有效的URL。然后,我可以, 不知道钥匙 ,将电子邮件地址修改为 victim@victim.com (并在此过程中混淆姓氏,这并不重要),并拥有一个有效的URL,我可以将其提交给您的服务器,并为我无法控制的电子邮件地址创建帐户。

    为了解决这个问题,你需要对数据计算一个HMAC,用只有服务器知道的秘密作为密钥,并将其作为URL的一部分发送。请注意,你需要加密的唯一原因是保护用户的密码,而不是它可能只是明文加上HMAC。我建议你只需将以下内容作为url发送:

    ?ln=Last%20Name&fn=First%20Name&email=foo@bar.com&hmac=7fpsQba2GMepELxilVUEfwl3%2BN1MdCsg%2FZ59dDd63QE%3D
    

    …并让验证页面提示输入密码(似乎没有理由来回弹出密码)。

        5
  •  1
  •   Filip Navara    16 年前

    我将尝试描述一个可能奏效的设计。

    先决条件:

    • 支持RSA和一些安全散列函数H(如SHA-1)的加密库

    设计:

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

    流程:

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

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