简体   繁体   English

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

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

In 2011, I bought a PHP contact form from codecanyon that uses AJAX to process the form. 2011 年,我从 codecanyon 购买了一个 PHP 联系表单,它使用 AJAX 来处理表单。 After reporting my problem to them today, they responded saying that they no longer offer support for their 'old' product (so much for the life time support they generally offer as a rule) ... so they aren't going to help me hence this post on SO.今天向他们报告了我的问题后,他们回复说他们不再为他们的“旧”产品提供支持(他们通常提供的终身支持这么多)......所以他们不会帮助我因此这篇关于SO的帖子。

I would say that this isn't a normal issue but I think it's very important that it gets sorted out - here it is (this is my email to the seller but does explain the problem):我会说这不是一个正常问题,但我认为解决它非常重要 - 在这里(这是我给卖家的电子邮件,但确实解释了问题):

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

I have an unusual issue with your AJAX Contact Form (you're going to have to read carefully and slowly).我对您的 AJAX 联系表有一个不寻常的问题(您必须仔细且缓慢地阅读)。

Okay everything works 100% fine BUT ... let me explain (basically this has everything to do with the Captcha and verification of it)好的,一切正常,100% 但是……让我解释一下(基本上这与 Captcha 和验证有关)

My website has many pages with your online form on each of those pages.我的网站有很多页面,每个页面上都有您的在线表单。 Now I also have a listings page that has links going to all of those pages with forms.现在我还有一个列表页面,其中包含指向所有带有表单的页面的链接。

EXAMPLE:例子:

Lets say I am on a listings page (a page with a whole load of links going to other pages) and I right click on Link A to open page A in a new tab ... and then I also right click on Link B to open page B in a new tab.假设我在一个列表页面(一个有大量链接到其他页面的页面),我右键单击链接 A 在新选项卡中打开页面 A ......然后我也右键单击链接 B 到在新选项卡中打开页面 B。 Right, so we have the listings page (that's still opened in front of me) and those 2 other pages that opened up in new tabs (Page A and Page B) ... as explained above, both those pages has your online form.是的,所以我们有列表页面(仍然在我面前打开)和其他 2 个在新选项卡中打开的页面(页面 A 和页面 B)......如上所述,这两个页面都有您的在线表格。

Now, I fill in both forms and click submit.现在,我填写两个表格并点击提交。

The first page that I right clicked to open in a new tab (Page A) - that form's Captcha doesn't work even when I've verified it correctly... however the form's Captcha on Page B does work (like it should).我右键单击以在新选项卡(页面 A)中打开的第一页 - 即使我已正确验证该表单的验证码也不起作用......但是页面 B 上的表单验证码确实有效(就像它应该的那样) . Why is it that the Captcha on Page A (the first page I opened) doesn't work?为什么 A 页面(我打开的第一页)上的验证码不起作用?

I get the feeling that in the whole verification system, because Page B was opened up last , the verification is taking that page's captcha code into account, using that captcha for verification (throughout the session surfing on my website) thus making the Captcha on the first opened page (Page A) to not work.我觉得在整个验证系统中,因为页面 B 是最后打开的,所以验证会考虑该页面的验证码,使用验证码进行验证(在我的网站上浏览整个会话期间),从而使验证码在第一次打开的页面(Page A)不起作用。

So what I did as an experiment:所以我做了一个实验:

I restarted and did the same thing again, IE: I right clicked Link A to open page A in a new tab ... and then I also right click on Link B to open page B in a new tab.我重新启动并再次做同样的事情,IE:我右键单击链接 A 在新选项卡中打开页面 A ......然后我也右键单击链接 B 在新选项卡中打开页面 B。

I filled in Page B's Captcha code in Page A's Captcha verification field and what do you know - there's a match!在页面 A 的验证码字段中填写了页面 B 的验证你知道什么 - 匹配!

So this is my problem because I know when some people surf internet (I do this all the time and maybe you do too), they like to right click links to open them in new tabs so that they can get back to them later after browsing the listings page.所以这是我的问题,因为我知道当有些人上网时(我一直这样做,也许你也这样做),他们喜欢右键单击链接以在新标签中打开它们,以便他们可以在浏览后返回列表页面。 So the person may have 6 tabs open in the browser and each of those pages has your online form.因此,此人可能在浏览器中打开了 6 个选项卡,并且每个页面都有您的在线表单。 If the user wants to submit each of those forms, then he/she will experience the exact problem I am reporting above.如果用户想要提交这些表单中的每一个,那么他/她将遇到我上面报告的确切问题。 They will be able to send through 1 form (the last page that was opened in a new tab) but the other page's Captchas won't work unless they refresh the page ... but most people won't think to do that - instead, they will think somethings wrong with the my website - which I am afraid of.他们将能够通过 1 个表单(在新选项卡中打开的最后一页)发送,但其他页面的验证码将无法工作,除非他们刷新页面......但大多数人不会想到这样做 - 相反,他们会认为我的网站有问题——我很害怕。

Is there a solution to this?这个问题有方法解决吗? I'm not even sure if you've noticed this before?我什至不确定你以前是否注意到这一点?

I hoped I've explained the situation clearly and I'd really appreciate it if you could assist.我希望我已经清楚地解释了情况,如果您能提供帮助,我将不胜感激。

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

Now back to you.现在回到你身边。 What's causing this?这是什么原因造成的?

There are 3 files needed for the form to work / process etc (I'm not including the CSS file in this post not the html for the form as I don't think it's necessary).表单工作/处理等需要 3 个文件(我不包括这篇文章中的 CSS 文件,而不是表单的 html,因为我认为没有必要)。

1) process.php 1) 进程.php

2) image.php (this is for the captcha) 2)image.php(这是用于验证码)

3) ajax.js 3) ajax.js

PROCESS.PHP进程文件

<?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!'; 

     }

   }

    ?>

*Please note that in the process.php code above, I removed a function that seems to validate the email address field - reason why I didn't include it in the code above is because it was heavy with code (would take up a lot of space) and I don't think it's necessary to include *请注意,在上面的 process.php 代码中,我删除了一个似乎验证电子邮件地址字段的函数 - 我没有在上面的代码中包含它的原因是因为它的代码很重(会占用很多空间),我认为没有必要包括

IMAGE.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.JS 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();
   });

   });

Looking at the code above, can anyone spot what could be causing this problem?看看上面的代码,谁能发现是什么导致了这个问题? I'm assuming this can has to do with the javascript?我假设这可能与javascript有关?

The comments are correct, the validation is failing on some forms because the session only holds the value of the last captcha generated therefore making captchas open in other tabs invalid because their value in the session was overwritten.评论是正确的,某些表单的验证失败,因为会话只保存最后生成的验证码的值,因此在其他选项卡中打开的验证码无效,因为它们在会话中的值被覆盖。 Because of this, anyone using the same or similar code has this problem.正因为如此,任何使用相同或相似代码的人都会遇到这个问题。

You can solve it fairly simply by changing the session to store an array of codes instead of just one.您可以通过更改会话来存储一组代码而不是一个代码来简单地解决它。

In image.php , change:image.php ,更改:

$_SESSION['verify'] = $hash_md5;

to:到:

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

You can also get rid of the cookie that gets set for the captcha, session storage should be fine.您还可以摆脱为验证码设置的 cookie,会话存储应该没问题。

Then in your form processor, change:然后在您的表单处理器中,更改:

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

to:到:

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

This should allow you to have multiple forms open in multiple tabs and still be able to submit each one without getting the incorrect captcha error.这应该允许您在多个选项卡中打开多个表单,并且仍然能够提交每个表单而不会出现不正确的验证码错误。

Also, another issue with this code is that it doesn't unset the session verify value after a successful post.此外,此代码的另一个问题是它不会在成功发布后取消设置会话验证值。 This means a person could solve one captcha and submit your form an unlimited number of times re-using the old code as long as they don't access image.php again between submissions.这意味着一个人可以解决一个验证码并无限次地重新使用旧代码提交您的表单,只要他们在提交之间不再访问 image.php 即可。

To fix this with the array version, you'll need to unset the session key after the captcha and form is processed.要使用数组版本解决此问题,您需要在处理完验证码和表单后取消设置会话密钥。

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

Hope that helps.希望有帮助。

I have an idea.我有个主意。 Store the captcha values in an array, and keep a counter;将验证码值存储在一个数组中,并保留一个计数器; both stored in SESSION variables.都存储在 SESSION 变量中。

So in the form you put a hidden input, and set it to the index.所以在表单中你放置了一个隐藏的输入,并将其设置为索引。

When we check for captcha, we compare $_SESSION['captcha'][$index] to $_POST['captcha'].当我们检查验证码时,我们将 $_SESSION['captcha'][$index] 与 $_POST['captcha'] 进行比较。

Any time you (the client) open a new window;任何时候你(客户)打开一个新窗口; $index is increased. $index 增加。 We pass that index to image.php through the url;我们通过 url 将该索引传递给 image.php; example src="img.php?index=2"示例 src="img.php?index=2"


Here is a concept;这是一个概念; minimal code to accomplish this.完成此操作的最少代码。 Open a couple of windows with this page.用这个页面打开几个窗口。 See what happens走着瞧吧

img.php 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);
?>

index.php索引.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