繁体   English   中英

PHP 验证码系统问题,AJAX 联系表

[英]Issue with Captcha system on PHP, AJAX Contact form

2011 年,我从 codecanyon 购买了一个 PHP 联系表单,它使用 AJAX 来处理表单。 今天向他们报告了我的问题后,他们回复说他们不再为他们的“旧”产品提供支持(他们通常提供的终身支持这么多)......所以他们不会帮助我因此这篇关于SO的帖子。

我会说这不是一个正常问题,但我认为解决它非常重要 - 在这里(这是我给卖家的电子邮件,但确实解释了问题):

==================

我对您的 AJAX 联系表有一个不寻常的问题(您必须仔细且缓慢地阅读)。

好的,一切正常,100% 但是……让我解释一下(基本上这与 Captcha 和验证有关)

我的网站有很多页面,每个页面上都有您的在线表单。 现在我还有一个列表页面,其中包含指向所有带有表单的页面的链接。

例子:

假设我在一个列表页面(一个有大量链接到其他页面的页面),我右键单击链接 A 在新选项卡中打开页面 A ......然后我也右键单击链接 B 到在新选项卡中打开页面 B。 是的,所以我们有列表页面(仍然在我面前打开)和其他 2 个在新选项卡中打开的页面(页面 A 和页面 B)......如上所述,这两个页面都有您的在线表格。

现在,我填写两个表格并点击提交。

我右键单击以在新选项卡(页面 A)中打开的第一页 - 即使我已正确验证该表单的验证码也不起作用......但是页面 B 上的表单验证码确实有效(就像它应该的那样) . 为什么 A 页面(我打开的第一页)上的验证码不起作用?

我觉得在整个验证系统中,因为页面 B 是最后打开的,所以验证会考虑该页面的验证码,使用验证码进行验证(在我的网站上浏览整个会话期间),从而使验证码在第一次打开的页面(Page A)不起作用。

所以我做了一个实验:

我重新启动并再次做同样的事情,IE:我右键单击链接 A 在新选项卡中打开页面 A ......然后我也右键单击链接 B 在新选项卡中打开页面 B。

在页面 A 的验证码字段中填写了页面 B 的验证你知道什么 - 匹配!

所以这是我的问题,因为我知道当有些人上网时(我一直这样做,也许你也这样做),他们喜欢右键单击链接以在新标签中打开它们,以便他们可以在浏览后返回列表页面。 因此,此人可能在浏览器中打开了 6 个选项卡,并且每个页面都有您的在线表单。 如果用户想要提交这些表单中的每一个,那么他/她将遇到我上面报告的确切问题。 他们将能够通过 1 个表单(在新选项卡中打开的最后一页)发送,但其他页面的验证码将无法工作,除非他们刷新页面......但大多数人不会想到这样做 - 相反,他们会认为我的网站有问题——我很害怕。

这个问题有方法解决吗? 我什至不确定你以前是否注意到这一点?

我希望我已经清楚地解释了情况,如果您能提供帮助,我将不胜感激。

==================

现在回到你身边。 这是什么原因造成的?

表单工作/处理等需要 3 个文件(我不包括这篇文章中的 CSS 文件,而不是表单的 html,因为我认为没有必要)。

1) 进程.php

2)image.php(这是用于验证码)

3) ajax.js

进程文件

<?php if (!isset($_SESSION)) session_start();

if(!$_POST) exit;

if (!defined("PHP_EOL")) define("PHP_EOL", "\r\n");

$address = "email@example.com";
$bcc = "email@example.com";

$name    = $_POST['name'];
$email  = $_POST['email'];
$phone  = $_POST['phone'];
$comments = $_POST['comments'];

if (isset($_POST['verify'])) :
    $posted_verify   = $_POST['verify'];
    $posted_verify   = md5($posted_verify);
else :
    $posted_verify = '';
endif;


$session_verify = $_SESSION['verify'];

if (empty($session_verify)) $session_verify = $_COOKIE['verify'];

$error = '';

    if(trim($name) == '') {
        $error .= '<li>Your name is required.</li>';
    }

    if(trim($email) == '') {
        $error .= '<li>Your e-mail address is required.</li>';
    } elseif(!isEmail($email)) {
        $error .= '<li>You have entered an invalid e-mail  
  address.</li>';
    }

    if(trim($phone) == '') {
        $error .= '<li>Your phone number is required.</li>';
    } elseif(!is_numeric($phone)) {
        $error .= '<li>Your phone number can only contain digits 
 (numbers and no spaces).</li>';
    }

    if(trim($comments) == '') {
        $error .= '<li>You must enter a message to send.</li>';
    }

    if($session_verify != $posted_verify) {
        $error .= '<li>The verification code you entered is 
 incorrect.</li>';
    }

    if($error != '') {
        echo '<div class="error_title"><h6><span>Attention!
</span> Please correct the errors below and try again</h6>';
        echo '<ul class="error_messages">' . $error . '</ul>';
        echo '<div class="close"></div>';
        echo '</div>';

    } else {

    if(get_magic_quotes_gpc()) { $comments = stripslashes($comments); }


     $e_subject = 'Booking / Enquiry';


     $msg = '<html>
<body style="margin:0; padding:0;">

Name: '.$_POST['name'].'
Email: '.$_POST['email'].'
Contact Number:  '.$_POST['phone'].'
Notes: '.$_POST['comments'].'


 </body>
 </html>';



    $msg = wordwrap( $msg, 70 );

    $headers = "From: $email\r\nBCC:{$bcc}\r\n" . PHP_EOL;
    $headers .= "Reply-To: $email" . PHP_EOL;
    $headers .= "MIME-Version: 1.0" . PHP_EOL;
    $headers .= "Content-type: text/html; charset=utf-8" .     PHP_EOL;
    $headers .= 'Content-Transfer-Encoding: 8bit'. "\n\r\n" .  PHP_EOL;

    if(mail($address, $e_subject, $msg, $headers)) {


     echo "<div class='success'>";
     echo "<h6>Your Enquiry has been Successfully submitted.  </h6>";
     echo '<div class="close"></div>';
     echo "</div>";


     } else {

     echo 'ERROR!'; 

     }

   }

    ?>

*请注意,在上面的 process.php 代码中,我删除了一个似乎验证电子邮件地址字段的函数 - 我没有在上面的代码中包含它的原因是因为它的代码很重(会占用很多空间),我认为没有必要包括

图像文件

<?php if (!isset($_SESSION)) session_start(); header("(anti-spam-
content-
type:) image/png");

$enc_num = rand(0, 9999);
$key_num = rand(0, 24);
$hash_string = substr(md5($enc_num), $key_num, 5); // Length of 
String
$hash_md5 = md5($hash_string);

 $_SESSION['verify'] = $hash_md5;


 setcookie("verify", $hash_md5, time()+3600, "/");

 session_write_close();



 $bgs = array("../../img/1.png","../../img/2.png","../../img/3.png");
 $background = array_rand($bgs, 1);


 $img_handle = imagecreatefrompng($bgs[$background]);
 $text_colour = imagecolorallocate($img_handle, 108, 127, 6);
 $font_size = 5;

 $size_array = getimagesize($bgs[$background]);
 $img_w = $size_array[0];
 $img_h = $size_array[1];

 $horiz = round(($img_w/2)-
 ((strlen($hash_string)*imagefontwidth(5))/2), 
 1);
  $vert = round(($img_h/2)-(imagefontheight($font_size)/2));



 imagestring($img_handle, $font_size, $horiz, $vert, $hash_string, 
 $text_colour);
 imagepng($img_handle);


 imagedestroy($img_handle);

 ?>

AJAX文件

   jQuery(document).ready(function() {
   $('.advertform').submit(function() {
    var action = $(this).attr('action');
    var form = this;
    $('.submit', this).attr('disabled', 'disabled').after(
          '<div class="loader"></div>').addClass("active");
    $('.message', this).slideUp(750, function() {
        $(this).hide();
        $.post(action, {
            name: $('.name', form).val(),
            email: $('.email', form).val(),
            phone: $('.phone', form).val(),
            comments: $('.comments', form).val(),
            verify: $('.verify', form).val()
        },
        function(data) {
            $('.message', form).html(data);
            $('.message', form).slideDown('slow');
            $('.loader', form).fadeOut('fast', function() {
                $(this).remove();
            });
            $('.submit', 
   form).removeAttr('disabled').removeClass("active");
        });
    });
    return false;
   });

   $('.message').on('click', function(){
    $('.message').slideUp();
   });

   });

看看上面的代码,谁能发现是什么导致了这个问题? 我假设这可能与javascript有关?

评论是正确的,某些表单的验证失败,因为会话只保存最后生成的验证码的值,因此在其他选项卡中打开的验证码无效,因为它们在会话中的值被覆盖。 正因为如此,任何使用相同或相似代码的人都会遇到这个问题。

您可以通过更改会话来存储一组代码而不是一个代码来简单地解决它。

image.php ,更改:

$_SESSION['verify'] = $hash_md5;

到:

if (!isset($_SESSION['verify'])) $_SESSION['verify'] = array();
$_SESSION['verify'][$hash_md5] = $hash_md5;  // *explantion for this array key later

您还可以摆脱为验证码设置的 cookie,会话存储应该没问题。

然后在您的表单处理器中,更改:

if($session_verify != $posted_verify) {
    $error .= '<li>The verification code you entered is incorrect.</li>';
}

到:

if(!array_key_exists($posted_verify, $session_verify)) {
    $error .= '<li>The verification code you entered is incorrect.</li>';
}

这应该允许您在多个选项卡中打开多个表单,并且仍然能够提交每个表单而不会出现不正确的验证码错误。

此外,此代码的另一个问题是它不会在成功发布后取消设置会话验证值。 这意味着一个人可以解决一个验证码并无限次地重新使用旧代码提交您的表单,只要他们在提交之间不再访问 image.php 即可。

要使用数组版本解决此问题,您需要在处理完验证码和表单后取消设置会话密钥。

unset($_SESSION['verify'][$posted_verify]); // remove code from session so it can't be reused

希望有帮助。

我有个主意。 将验证码值存储在一个数组中,并保留一个计数器; 都存储在 SESSION 变量中。

所以在表单中你放置了一个隐藏的输入,并将其设置为索引。

当我们检查验证码时,我们将 $_SESSION['captcha'][$index] 与 $_POST['captcha'] 进行比较。

任何时候你(客户)打开一个新窗口; $index 增加。 我们通过 url 将该索引传递给 image.php; 示例 src="img.php?index=2"


这是一个概念; 完成此操作的最少代码。 用这个页面打开几个窗口。 走着瞧吧

img.php

<?php 
session_start(); 
header("(anti-spam-content-type:) image/png");

$captcha_text = rand(0, 99999);
// we read a "index" from the URL, example: <img src="img.php?index=2">
$index = isset($_GET['index']) ? (int) $_GET['index'] : 0;

if( empty($_SESSION['captcha'])) {
  $_SESSION['captcha'] = array();
}
$_SESSION['captcha'][$index] = $captcha_text;

// @see http://php.net/manual/en/function.imagestring.php , first example
$im = imagecreate(100, 30);
$bg = imagecolorallocate($im, 55, 255, 255);
$textcolor = imagecolorallocate($im, 0, 0, 255);
imagestring($im, 5, 0, 0, $captcha_text, $textcolor);
header('Content-type: image/png');
imagepng($im);
imagedestroy($im);
?>

索引.php

<?php
session_start(); 
// we handle the POST
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_SESSION['captcha'])) {
    if ($_SESSION['captcha'][ $_POST['index'] ] == $_POST['captcha']) {
      echo '<h2>correct</h2>';
    }
    else {
      echo '<h2>not correct</h2>';
    }
    echo '<a href="index.php">Back to form</form>';
    // header('location: index.php');
    exit;
}

// normal page, with form
if(isset($_SESSION['captcha_index'])) {// index
  // set a new index  
  $_SESSION['captcha_index']++;
}
else {
  $_SESSION['captcha_index'] = 0;
}
$captcha_index = $_SESSION['captcha_index']; 

echo '
<img src="img.php?index=' . $captcha_index . '">
<form action="" method="post">
  <input name="captcha">
  <input name="index" type="hidden" value="' . $captcha_index . '">
  <input type="submit" value="GO">
</form>
'; 

// we show what's happening.  Obviously you don't want to print this after test phase
$captcha = isset($_SESSION['captcha']) ? $_SESSION['captcha'] : array();
echo '
<br>print_r of $_SESSION[captcha] 
<pre>' . print_r($captcha, true) . '<pre>
';
?>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM