【问题标题】:PHP Captcha without session没有会话的 PHP Captcha
【发布时间】:2010-11-30 17:51:38
【问题描述】:

好的,这是一个问题:在我正在处理的项目中,我们不能依赖服务器端会话来实现任何功能。

问题在于,防止机器人提交的常见验证码解决方案需要会话来存储字符串以匹配验证码。

问题是 - 有没有办法在不使用会话的情况下解决问题?我想到的是提供隐藏的表单字段,包含一些哈希以及验证码输入字段,以便服务器可以将这两个值匹配在一起。但是我们如何才能使这种方法安全,使其不能用于轻易破解验证码。

【问题讨论】:

  • 为什么不能依赖会话?
  • 我想问同一个会话。此外,没有会话意味着根本没有用户标识。您的 PHP 脚本变得无状态。在这个系统中验证码有什么用?
  • 他们将登录信息放在 GET 或 POST 变量中;)
  • 也许只是发送一个“联系我们”表格。

标签: php captcha


【解决方案1】:

对会话或数据库的需求来自于将图像的 GET 与包含它的 html 页面协调的需要,那么如何使用相同的代码嵌入验证码图像:[img src='data:image/jpeg ;base64,...'],使用随机盐对其文本进行散列,然后在单个 GET 中将随机盐和散列与图像一起发送到客户端?

在回发时,您将用户文本附加到 salt,然后比较哈希值。只是想知道这有多安全......

【讨论】:

    【解决方案2】:

    使用honeypottechnique:将具有贪心名称的文本字段(如“电子邮件”)放入由 CSS 隐藏的字段中(显示:无;可见性:隐藏;)。

    当您必须清理表单时,只需检查该字段是否为空、是否由人发送(看不到该字段,因此无法填写),否则,来自垃圾邮件发送者。

    这就是为什么垃圾邮件发送者通常在发送表单之前使用预先确定的值填充页面中的所有字段......并且不会打扰用户阅读验证码。

    否则,依靠人类阅读,例如“在字段中写单词“$word”的第一个$x字母:”

    然后,您只需将 $x 和 $word 发送到下一页并检查它(当然,您可以将字段名称随机化以更准确)

    我记得phpBB 论坛插件 依赖于这样一个事实,即垃圾邮件机器人通常会选择<select> 字段中的第一个可用选项(带有值);只需将<option value="kickmeplease">Yes, im a bot.</option>作为第一个选项@

    有很多方法可以防止垃圾邮件机器人,发挥机器人永远不会拥有的一个因素:想象力

    【讨论】:

    • 蜜罐并不是一个特别有用的解决方案,除了晦涩难懂的低价值目标。这是一种通过默默无闻的安全形式。它不能解决定制设计的攻击。即使对于非定制设计的攻击,它也假设机器人不会开发识别不可见字段的能力,这是相当微不足道的。如果蜜罐被有价值的目标使用,机器人将被调整为解释 CSS 并忽略不可见的字段。真正的验证码可以解决所有情况,除了不存在保护的“人类血汗工厂验证码求解器方法”(可能 IP 黑名单除外)。
    【解决方案3】:

    您可以尝试在数据库中存储一堆验证码。或者,这里有一个关于替代验证码方法的很好的讨论:Practical non-image based CAPTCHA approaches?

    确实有一些非常有趣的技术,请仔细阅读。

    【讨论】:

      【解决方案4】:

      让 CAPTCHA 生成器返回图像,并使用加盐散列或自定义散列作为答案(强调加盐/自定义)。让生成器将该哈希推送到 cookie 中。然后,服务器可以根据 cookie 中的值进行验证。这不需要 JavaScript,但如果 cookie 被禁用,您将不得不回退到另一种技术。

      【讨论】:

      • 哈希可以被推入一个隐藏的表单域。这样你就不需要 cookie。
      • @Matt 这是个好主意,但我认为这不安全。攻击者可以对已知结果使用已知哈希(例如 d9a263b9de1023a8cc... = "928137"),修改隐藏字段并使用已知结果提交。从而绕过验证码。
      • @cherouvim 这就是为什么你需要给你的哈希加盐 ;)
      • @matt 如果您在 HTML 源上给我一个加盐哈希作为隐藏输入,并且您还给我相应的验证码作为图像,是什么阻止我“重播”那个确切的哈希每当我需要绕过验证码时的验证码值?
      • @cherouvim 这是一个有效的观点。这个问题的一种解决方案是使用随机数——一种唯一的一次性令牌——只能验证一次,直接与验证码答案相关联。我可能还会在我的加盐算法中使用随机数,这意味着除了随机数是一次性使用之外,随机数令牌和验证码答案哈希从根本上是联系在一起的。类似于 XSRF 预防。
      【解决方案5】:

      自动填充验证码的 UUID 以及 POST 中的用户答案。十分简单。

      【讨论】:

        【解决方案6】:

        只需做一个数学验证码;) 2+90 = ?方程应该显示在图像中,瞧;)

        【讨论】:

        • 但是您仍然必须在服务器端存储方程式并识别发件人以进行验证。这不是关于挑战,而是关于交付。
        • 我不知道“存储方程式服务器端”是什么意思,方程式是由 PHP 返回的,无论如何它是服务器端的。
        【解决方案7】:

        使您的隐藏输入字段只是一个随机序列。将此随机数据与验证码信息一起存储在数据库中,以便您可以使用它查找正确的验证码。

        您还需要为生成的每个验证码设置一个较短的生存时间。最后,您可以在数据库中存储和跟踪每个验证码的尝试次数,并对其施加硬限制(3 次猜测,这是永久失败)。

        【讨论】:

        • 这是会话工作原理的基础。将数据存储在服务器端(您在数据库中的验证码信息)并将其与 ID(您的随机序列)相关联。
        【解决方案8】:

        如果没有持久状态服务器端,我看不到验证码工作。

        您的建议并不安全,因为攻击者总是可以很容易地发布自己的“隐藏字段”和匹配的验证码文本。

        为什么不从另一个可以保持持久状态的网络服务器进行验证码?

        【讨论】:

        • 是的,我知道如果服务器上没有某种形式的持久性,攻击者可以使用相同的正确哈希验证码对,例如 1000 亿次。但是如果我们用服务器的时间戳加盐哈希,幸运对会每 10-20 分钟过期一次,你觉得这种方法的安全性如何?
        • 它确实提供了更高级别的安全性,但我担心盐不够随机。要考虑的一件事是您需要的安全级别。如果您需要“一些”安全性,但成为攻击目标的可能性很低,您可以尝试这种安全性较低的方法。
        【解决方案9】:

        您能否授予他们客户端证书以响应 CAPTCHA 调用?然后,一旦他们在浏览器中选择了该证书,该证书就会随客户端的每次调用一起发送,并且可以在没有会话和进一步 CAPTCHA 调用的情况下用于身份验证。

        【讨论】:

        • 这是一种类似的证书,但它由客户端而不是服务器持有。使用 SSL,服务器提供证书并且客户端接受它。对于客户端证书,情况正好相反——客户端呈现(通过 Web 浏览器)并且 Web 服务器接受。在 Web 浏览器端选择客户端证书后,每次调用(Request["ClientCertificate"] 或类似的)都会发送该证书,因此您不必在会话中保持登录状态。但是,它确实增加了一些让客户跳过的障碍,并且可能对您不起作用。
        【解决方案10】:

        这是我的看法(如果看起来很复杂,请抱歉):

        1. 页面请求:

          • 你生成一个随机字符串代码'abcdef';
          • 您使用一些预定义的密码来加密代码: $crypt = encrypt($captcha_code, '密码')
        2. 形式:

          • 一个图片链接被发送到浏览器'captcha.php?$crypt'
          • 隐藏输入设置为 $crypt 的值
        3. captcha.php 页面对密文进行解密,生成图片。

        4. 用户提交带有代码“abcdaa”的表单(以及隐藏的输入 $crypt)

        5. 服务器验证是否 encrypt('abcdaa') == $crypt

        编辑: 加密函数需要是可逆的(解密),因为验证码图像生成器需要原始代码。

        【讨论】:

        • 这种方法的问题是攻击者可以多次使用一对值。
        • @Anton N:不完全是,如果密码更改(只需根据用户 IP 地址、当前日期、浏览器信息或其他一些信息等可用信息添加盐)
        • 我认为这种方法比 Anton N 最初建议的方法更糟糕。显然,使用哈希! hidden_​​field=hash(captcha + salt) 并通过 hidden_​​field == hash(user_inputted_captcha + salt) 进行验证。
        • @Yannick M. 问题在于图像生成脚本首先需要知道要生成什么文本代码。如果没有服务器端使用 session/db,文本必须来自某个地方...
        • 图片生成是服务器端的,文本+盐的hash只是为了验证之后的结果。关于加密的事情是,如果攻击者可以破解它,他们就会拥有文本+密码。虽然他们无法“破坏”哈希,但他们只能找到可能的冲突。
        【解决方案11】:

        这个解决方案怎么样?我在 google 上找到了这篇“Sessionless PHP Captcha”文章,并在我的一个项目中使用过,它很简单,没有会话,而且是免费的。 RC4 有什么安全问题吗?

        http://www.mythos-rini.com/blog/archives/732

        【讨论】:

          【解决方案12】:

          自己的想法,不知道好不好:

          1) 如果用户已登录,只需在登录时使用一些哈希函数并生成验证码,

          2)如果它是注册表单等,只需从表单字段中散列一些值(例如登录,当用户完成输入时)并通过 ajax 显示带有登录散列的 CAPTCHA。

          希望,这是可以理解的。 :)

          编辑: 没有 AJAX: 2步注册:

          在1,我们收集登录等提交后,我们直接到?login=new_login

          在 2 处,我们隐藏了带有 GET["login"] 的输入,并在 CAPTCHA 图像中对其进行哈希处理 - 提交后,我们必须检查答案。

          【讨论】:

          • 2) 好主意 :) 但这样做时,我们要么阻止人们关闭 javascript,要么允许所有没有 js 引擎的机器人每次都通过验证码。纠正我,如果我错了。
          • 机器人不会通过验证码,除非他们知道你如何散列登录:)
          • 是的,但是不会进行 ajax 调用的真实客户也没有机会通过?
          • 嗯,我喜欢它,尤其是结合起来 - 为启用 ajax 的客户即时制作验证码,并为所有其他客户使用带有两步形式的计划 b。会考虑这种方法:)
          • 如果你不要忘记给我的答案投票 +1 或者只是将其标记为解决方案会很好,THX(我注意到你是新来的):-)
          【解决方案13】:

          带有验证的表单:

          $errorsucc = '';
          
          if (isset($_POST["captcha_check"])) {
          
              $code = str_decrypt($_POST["captcha_check"]);   
          
              if (empty($_POST['captcha_code'])) { 
                  $errorsucc = '<p style="color:red">Please Enter the security code.</p>';
          
              } elseif(!( $code == $_POST['captcha_code'] && !empty($code) )) {
                  $errorsucc = '<p style="color:red">Incorrect Code Entered.</p>';
          
              } else {
                  $errorsucc = '<p style = "green">Nice, you entered the correct code.</p>';  
              }
          }
          
          $captcha = new CaptchaCode();
          $code = str_encrypt($captcha->generateCode(6));
          ?>
          
          <html>
              <title>Sessionless Captcha</title>
              <div style = "background: #e2e2e2; padding: 20px; width: 20%; box-shadow: 5px 5px #ccc;">
                  <?php echo $errorsucc; ?>
                  <form name="captchaform" method="post">
                      <table border="0" cellpadding="4" cellspacing="0">
                          <tr><td valign="middle" align="left">Security Code:</td>
                              <td valign="middle" align="left"><img src="captcha_images.php?width=150&height=50&code=<?php echo $code?>" /></td>
                          </tr>
                          <tr><td valign="middle" align="left">Enter Code:</td>
                              <td valign="middle" align="left"><input id="captcha_code" name="captcha_code" style="width:150px" type="text" /></td>
                          </tr>
          
                          <tr><td valign="top" align="left">
                              </td>
                              <td valign="top" align="left">
                                  <input border="0" type="submit" value="Submit" />   
                              </td>
                          </tr>
                      </table>
                      <input type="hidden" name="captcha_check" value="<?php echo $code?>" />
                  </form>
              </div>
          </html>
          

          像任何其他验证码一样生成图像:

          /* font size will be 75% of the image height */
              $font_size = $height * 0.75;
              $image = @imagecreate($width, $height) or die('Cannot initialize new GD image stream');
              /* set the colours */
              $background_color = imagecolorallocate($image, 255, 255, 255);
              $text_color = imagecolorallocate($image, 0, 26, 26);
              $noise_color = imagecolorallocate($image, 25, 89, 89);
              /* generate random dots in background */
              for( $i=0; $i<($width*$height)/3; $i++ ) {
                  imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
              }
              /* generate random lines in background */
              for( $i=0; $i<($width*$height)/150; $i++ ) {
                  imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);
              }
              /* create textbox and add text */
              $textbox = imagettfbbox($font_size, 0, $this->font, $code) or die('Error in imagettfbbox function');
              $x = ($width - $textbox[4])/2;
              $y = ($height - $textbox[5])/2;
              imagettftext($image, $font_size, 0, $x, $y, $text_color, $this->font , $code) or die('Error in imagettftext function');
              /* output captcha image to browser */
              header('Content-Type: image/jpeg');
              imagejpeg($image);
              imagedestroy($image);
          

          从此链接下载演示文件: Create a Sessionless Captcha in PHP

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-04-14
            • 2018-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-12
            相关资源
            最近更新 更多