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

时间:2009-09-05 08:31:41

标签: ruby-on-rails ruby database encryption encoding

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

编辑: 我做了一个有效的例子,网址是127个字符。

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 个答案:

答案 0 :(得分:6)

思想:

  • 对“cookie”使用真正的非对称密码来防止机器人创建帐户。使用公钥加密“cookie”,通过私钥解码验证它。
    原理:如果只使用base64或其他算法对cookie进行编码,则可以轻松地对方案进行反向工程并自动创建帐户。由于垃圾邮件,这是不可取的。此外,如果帐户受密码保护,则密码必须出现在cookie中。任何有权访问注册链接的人都不仅可以激活帐户,还可以找出密码。

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

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

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

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

答案 1 :(得分:4)

  • 有些电子邮件客户端会在80个字母后断开网址。我怀疑你能否掌握所有信息。

  • 某些浏览器对网址有限制,例如Internet Explorer 8的限制为2083个字符。

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

答案 2 :(得分:3)

之前我做过几乎一样的事情。我只有2条建议,

  1. 添加密钥版本,以便您可以旋转密钥而不会破坏未完成的确认。
  2. 您需要时间戳或到期时间,以便在需要时设置确认时间限制。我们允许一周。
  3. 关于最短的网址,您可以通过进行以下更改来做得更好,

    1. 使用像CFB这样的流密码模式,这样您就不必填充块大小。
    2. 当数据很大时,压缩明文​​将有所帮助。我有一个标志,只在收缩数据时才使用压缩。
    3. 使用Base64.urlsafe_encode64(),因此您无需对其进行网址编码。

答案 3 :(得分:3)

您的解决方案存在一些问题。

首先,您没有设置密码的IV。在我看来,这暴露了Ruby OpenSSL包装器中的一个严重错误 - 在设置keyiv之前,它不应该让你执行加密或解密,而是继续使用它全零的IV。每次使用相同的IV基本上消除了首先使用反馈模式的许多好处。

其次,更严重的是,您没有真实性检查。 CBC模式的一个属性是,有权访问一条消息的攻击者可以修改它以创建第二条消息,其中第二条消息中的块具有完全受攻击者控制的内容,代价是前一块完全出现乱码。 (哦,请注意CFB模式在这方面也是一个问题)。

在这种情况下,这意味着我可以申请姓名为AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA的帐户和我自己的电子邮件地址来接收有效的网址。然后,我可以在不知道密钥的情况下,将电子邮件地址修改为victim@victim.com(并在过程中乱写姓氏,这无关紧要),并且有一个有效的URL我可以提交到您的服务器并为我无法控制的电子邮件地址创建帐户。

要解决此问题,您需要计算数据上的HMAC,使用只有服务器知道的秘密来键入,并将其作为URL的一部分发送。请注意,这里你需要加密的唯一原因是保护用户的密码 - 除了它可以只是纯文本加HMAC。我建议您只需将网址发送为:

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

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

答案 4 :(得分:1)

我会在描述可能有效的设计时采取行动。

<强> Prerequisities:

  • 支持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中相同的值进行哈希处理)以及创建帐户所需的任何其他信息
  • 提交激活页面后,将创建一个新帐户

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