Captcha哈希cookie的加密安全性

时间:2009-10-26 06:20:34

标签: security authentication cryptography captcha

我公司的CRM系统在每次登录时都使用验证码系统,以便利用某些管理功能。原始实现将当前验证码值存储在服务器端会话变量中。

我们现在需要重新开发它以在散列的客户端cookie中存储所有必要的验证码验证信息。这是由于父IT策略旨在通过禁止对尚未对应用程序进行身份验证的用户使用会话来减少开销。因此,不允许认证过程本身使用服务器端存储或会话。

设计是一种集体努力,我对其整体功效有疑问。我的问题是,任何人都可以看到下面显示的实现存在任何明显的安全问题,并且它是否有任何技术上的过度或不足?

编辑:进一步的讨论导致了更新的实现,所以我用新版本替换了原始代码,并编辑了描述以与此版本进行对话。

(下面的代码是一种伪代码;原始代码使用了一些特殊的遗留库和结构,这使得它很难阅读。希望这种风格很容易理解。)

// Generate a "session" cookie unique to a particular machine and timeframe
String generateSessionHash(timestamp) {
    return sha256( "" 
        + (int)(timestamp / CAPTCHA_VALIDITY_SECONDS)
        + "|" + request.getRemoteAddr() 
        + "|" + request.getUserAgent() 
        + "|" + BASE64_8192BIT_SECRET_A
    );
}

// Generate a hash of the captcha, salted with secret key and session id
String generateCaptchaHash(captchaValue, session_hash) {
    return sha256( ""
        + captchaValue
        + "|" + BASE64_8192BIT_SECRET_B
        + "|" + session_hash
    );
}

// Set cookie with hash matching the provided captcha image
void setCaptchaCookie(CaptchaGenerator captcha) {
    String session_hash = generateSessionHash(time());
    String captcha_hash = generateCaptchaHash(captcha.getValue(), session_hash);
    response.setCookie(CAPTCHA_COOKIE, captcha_hash + session_hash);
}

// Return true if user's input matches the cookie captcha hash
boolean isCaptchaValid(userInputValue) {
    String cookie = request.getCookie(CAPTCHA_COOKIE);
    String cookie_captcha_hash = substring(cookie, 0, 64);
    String cookie_session_hash = substring(cookie, 64, 64);
    String session_hash = generateSessionHash(time());
    if (!session_hash.equals(cookie_session_hash)) {
        session_hash = generateSessionHash(time() - CAPTCHA_VALIDITY_SECONDS);
    }
    String captcha_hash = generateCaptchaHash(userInputValue, session_hash);
    return captcha_hash.equals(cookie_captcha_hash);
}

概念:

  1. “session_hash”旨在防止在多台计算机上使用相同的Cookie,并强制执行一段时间后无效。
  2. “session_hash”和“captcha_hash”都有自己的秘密盐键。
  3. 这些BASE64_8192BIT_SECRET_A和_B salt密钥是存储在服务器上的RSA私钥的一部分。
  4. 使用秘密和“session_hash”对“captcha_hash”进行腌制。
  5. 在使用客户提供的数据的地方添加分隔符,以避免拼接攻击。
  6. “captcha_hash”和“session_hash”都存储在客户端cookie中。
  7. 编辑:re:Kobi感谢您的反馈!

    (我会在评论中回复,但它似乎不接受在问题中有效的格式?)

    每次访问登录页面时,都会更换验证码;然而,这确实假设他们不是简单地重新提交而不重新加载登录表单页面。基于会话的实现使用到期时间来避免此问题。我们还可以在登录页面添加一个nonce,但我们也需要服务器端会话存储。

    根据Kobi的建议,过期时间帧现在包含在散列数据中,但一致意见是将其添加到session_hash,因为会话具有超时是直观的。

    这种散列一些数据并在该数据中包含另一个哈希的想法似乎对我很怀疑。是否真的有任何好处,或者我们最好使用包含所有相关数据(时间,IP,用户代理,验证码和密钥)的单个哈希。在这个实现中,我们基本上是告诉用户部分的散列明文。

    问题:

    1. 有没有明显的缺陷?
    2. 有任何微妙的缺陷吗?
    3. 是否有更强大的方法?
    4. 用另一个哈希帮助任何东西腌制哈希?
    5. 是否有更简单,同样强大的方法?
    6. 新问题:

      我个人认为我们最好将其作为服务器端会话;任何人都可以向我指出任何证明或反驳仅向客户端发送所有验证数据的固有风险的论文或文章吗?

1 个答案:

答案 0 :(得分:3)

假设没有其他安全措施: 似乎攻击者可以解决验证码一次,并保存cookie 然后,她经常session_hashcaptcha_hash。没有什么能阻止她使用相同的散列数据提交相同的cookie - 可能会破坏您的系统 使用time作为captcha_hash的一部分可以避免这种情况(您需要将其舍入到偶数时间,可能是几分钟 - 并检查两个选项 - 当前时间和之前的时间)

为此,你说:

  

“session_hash”旨在防止在多台计算机上使用相同的cookie。

这是真的吗? 在isCaptchaValid您正在String session_hash = substring(cookie, 64, 64); - 这就是:您依赖于Cookie中的数据。你怎么能告诉它没有从另一台电脑复制? - 您没有再次对客户端数据进行哈希处理以确认它(事实上,那里有一个随机数,因此可能无法实现)。你怎么能告诉它新的要求,并没有被使用过?

我意识到每次登录都会替换验证码,但是如何在发出请求时知道? 您没有检查isCaptchaValid 上的新验证码 - 您的代码仍将验证请求,即使与显示的验证码不匹配。
考虑以下场景(可以自动化):

  1. Eve打开登录页面。
  2. 获取一个新的cookie和一个新的验证码。
  3. 用旧的饼干取代它,并用她旧的cptcha的散列数据。
  4. 提交旧Cookie,userInputValue使用旧验证字。
  5. 通过此输入,isCaptchaValid验证请求 - captcha_hashsession_hashuserInputValueBASE64_8192BIT_SECRET与第一次请求时的相同
  6. 顺便说一句,在大多数系统中,无论如何你都需要一个nonce,以避免使用XSS,并且还可以解决你的问题。