Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having much trouble inserting PHP-based reCaptcha into a jQuery-based Shoutbox

This is my first time asking on Stack Overflow (however, not my first making use of the good responses on other people's questions). I'm an empiric website programmer, so rest assured I always try till the wee hours of dawn to work ways around the lack of proper instruction. That's when Stack Overflow comes in real handy. But, this time, I've been hitting my head against a PHP wall for over three or four days. So I'm asking out for some aid on the matter.

Now, programming is purely math, in essence. Which is why one can eventually understand its language and what's going on. I reached the point of being a very experienced AS programmer and then I decided to switch from Flash, try my hand at HTML using JS and jQuery (the competition, so to speak) and fell in love. Still, am rather an amateur in that field. But my relationship to PHP in particular is of a completely different sort: I don't understand it and have no patience for it; it's Sanskrit to me. Maybe I haven't used it enough to get the hang of it, but it seems not to make logical sense to me, as JS and HTML and AS and ETC. do.

Anyway, my predicament:

I've built a custom Shoutbox based on the great code given here: http://yensdesign.com/2009/01/create-a-shoutbox-using-php-and-ajax-jquery/. However, the code doesn't include a captcha, which makes it completely vulnerable to SPAM. So I first designed a simple captcha consisting of various images that contain numbers, all adding up to a specific number. Now I'm trying to go fancy and I've been trying to add Google reCaptcha.

ReCaptcha is PHP-based. The Sbox does use PHP but once a JS file does the checking-of-the-form stuff; it uses PHP to finally save the data entered in a mySql database and retrieve previously saved data to be displayed. I've tried putting the reCaptcha PHP code in this part of the process, but it doesn't really work, as all it does is stop the data from being entered and stop it from being retrieved. What I need is something which stops the process at the beginning, as by saying: "Hey! You've entered the wrong Captcha, you Spam-bot, you!"

I've also tried putting the PHP within the JS file. . . Finally, after pulling a great deal of hair, have come upon a sentence which says: "JS is client-based; PHP is server-based." Great info I could have come upon days ago. Amateur me, now I get why PHP is so fussy.

Anyway, I've tried placing PHP up and down and in an out and have gotten nil, naught, zero.


I hope I've given enough background on the issue. Feedback on my first Stack Overflow question will be helpful. And especially helpful: any aid on cracking this mystery you can toss my way.

The reCaptcha PHP code is the following:

<?php

$privatekey = "your_private_key";
$resp = recaptcha_check_answer ($privatekey,
       $_SERVER["REMOTE_ADDR"],
       $_POST["recaptcha_challenge_field"],
       $_POST["recaptcha_response_field"]);
if (!$resp->is_valid) {
       // What happens when the CAPTCHA was entered incorrectly
       die ("The reCAPTCHA wasn't entered correctly. Go back and try it again." .
       "(reCAPTCHA said: " . $resp->error . ")");
} else {
       // Your code here to handle a successful verification
}

?>

And that's about it on the reCaptcha, save for styling. Seems simple enough!

I think the Shoutbox codes are too many and too long to include here and saturate the post. But they're at link given above. They're basically the HTML Shoutbox, its JS file and the PHP info save & retrieve.

Also, for further reference on it, I'm attaching a link to a RAR-file containing my work on the Shoutbox, as is, to this point. As you will see, at the bottom of the page there's a preloader. It should turn off as a list of previous messages appears. It doesn't work because of the darned PHP I fumblingly placed inside the JS file--which no doubt is returning and error that stops the JS sequence. If you comment the PHP or delete it, then the sequence should be completed and at least a "can't find db... bla bla" message should appear as the preloader disappears, meaning the Shoutbox is functional (but without a captcha).

And the RAR-file, voilà: (No longer available)

In advance, thanks a lot for the patience and for any aid you can send my way!


EDIT 1:

Ok, here goes an update following J. Bruni's instructions for the exercise detailed below.

So, following J. Bruni's recommendations, assuming I've understood them correctly, I've taken out only the iframe. I've left the rest of the reCaptcha intact as it is superfluous to this exercise (besides, I need the Captcha image in order to test out whether the thing is actually working). The form, originally, is not set to post to a specific output file, as you can see (reason being that it has the JS file for that), but, as J. Bruni's recommended exercise goes, I've set it to action="shoutbox.php". This means it is now bypassing the JS file entirely.

Testing: I get the WRONG WORDS and RIGHT WORDS messages J. Bruni coded into the original reCaptcha PHP code accordingly, so everything is working perfect right now, at least reCaptcha related.

However, the JS file is completely bypassed, as I said. One of the initial late-night tests I did, I tried something similar to what J. Bruni proposes. I tried to return a true/false statement via this reCaptcha code at the top of the PHP file so it could be conditioned, together with the true/false returned by the function checking all fields are filled in (client-side, within the JS file), in order to proceed if all true or return an error message if any false. The problem I encountered there, as you may probably guess, but I understood only later on, was that one check is done client-side and the other is done server-side; how can you couple them both? (If you even can.) My question here would be: Is there a way of running both the PHP and JS 'parallel' and then bringing together their respective results? Already it sounds impossible; being one client-side, it sounds immediate, while the other, server-side, sounds remote.

Perhaps a keener programmer will, moreover, at this point say: Why take care of both checks separately? Seems like very messy and unnecessary; they could both be in PHP. Alas! The thing is, though I know how to post the form's input text variables straight to the PHP file, I have no idea how to translate into PHP the rest of it! Don't even know if it can be translated. Really, if I had my pick, I would rather find a way of making it all JS. But then there's what J. Bruni wisely points out:

You need some server-side code, because you can't show your private key to the public... A tech user would very easily bypass the whole verification, if everything is done client-side, and nothing server-side.

So, after the first late-night exercise I explained above resulted in a fiasco, I tried conditioning the rest of the shoutbox.php code to the reCaptcha function returning true. If it returned false, the PHP code would stop there. However, this meant that no previous messages would be displayed in the Shoutbox before a new form was submitted; and, even then, this would be conditioned to the form being submitted effectively (meaning the reCaptcha was entered correctly). If the form returned a false, then nothing would happen, save the reload of the page—which, we can agree, is impractical for many reasons. On the other hand, separating both parts of the PHP code, making one run independent from the reCaptcha condition and the other conditional to it, didn't really make much difference.

It was then that I tried inserting the reCaptcha PHP check into the JS file, which seemed to me the only way out. And we all know how that ended! It got me here and at your mercy. And, as J. Bruni wisely pointed out, something I hadn't though about, having my private ID client-side offered it out to the world unprotected.

So, after J. Bruni's proposed exercise, I now have that the reCaptcha works fine per se. If the form is posted directly to the PHP file, and if the parts in the JS file which take care of gathering the form's values and checking if they're correct are transcribed into the PHP file, then I guess the Sbox may work at a basic level. But, right now, and without the reCaptcha, the Sbox is working 100%, all fancy and whatnot. There has to be a way of inserting that darned reCaptcha and still have the Sbox working topnotch. Now I've got to figure out how to make a sort of 'detour' that allows the JS file to work its magic as well, while the PHP does its job. Though, to be honest, I'm not sure which is more practical: first checking if forms are filled in and returning an error if they aren't before even checking if the reCaptcha has been entered correctly, or the other way around. One could always slap them together, as I tried doing in the first example late-night trial I told you about above; but, of course, it's a lot fancier to check them separately, so you can tell your user where exactly he's messing up. What I do know is that it sounds more practical to stop the sending of the form at the very first instance, upon finding en error or incorrect input in either the form or the reCaptcha, rather than letting the data get all the way to the database door before its validity is checked. Maybe it's a moot point, taking into consideration this all is done in fractions of a second, but it just sounds messy. What do you think?

Anyway, I'll try to crack this in the next couple of days and let you know how I do. Nonetheless, if anyone's light bulb flashes on in the meantime, be my guest to scramble things up and show me another possibility of light at the end of this tunnel.

NOTE: By the way, I've realized (at least when posting straight to the PHP) the reCaptcha needs and extra script which calls for it to be refreshed each time the page is brought back any other way than by refreshing, say by pressing BACK once the result is printed. Otherwise, the captcha image remains the same but, apparently, the reCaptcha believes it's showing another challenge; if you try answering the same reCaptcha with the same image you may already know to be correct, it will return an false result. This to me means that the challenge is always reloaded automatically but, for some reason, the image goes unchanged in this specific case. Just a heads up.


EDIT 2

Ok, as J. Bruni has noticed, we both overlooked an "!" in the if statement in the PHP-file. This has now been corrected.

HOWEVER... yes, however, another another issue arises.

Hmmm. The if statement has been corrected and now it does return the 'wrong captcha' alert. But for some reason the reCapthca is always returning 'wrong', no matter what. Yesterday, the form was always printing, even if there was no captcha or a wrong captcha inserted because the if statement, as Bruni noted, was set to return a positive for the captcha being entered wrong, in which case, the form would go ahead and print if the captcha was wrong (but not if it was right). It always was wrong, which is why it printed all the time. Correcting the if statement to return positive when the captcha is correct has made evident that the captcha is always returning a wrong answer because now you always get the 'wrong captcha' alert. So this specific issue must be somewhere else.

Now, as I've stated in various occasions, I'm as good as lost in PHP. So I couldn't say if the if/else statement in the PHP is correct. But if my other experience serves me, it does seem like the if statement in the JS-file could be lacking something that makes it alert and stop the rest of the complete: function(data) function, because it seems to me that the rest of the complete: function(data) function is always being played. In any case, seems weird to me not to put the alert() in brackets as a result of the if statement. Maybe, even if the if statement is wrong, still the alert() and the rest of the function is being played because it needs brackets... But because this part is AJAX I could be speaking gibberish and barking up the wrong tree. Please correct me if I'm wrong.

I'll try to solve it and post back where the problem was hiding. And when I get a 100% functioning Sbox going, I'll post the final product here so it's available to all who may encounter the same or a similar problem.


EDIT 3

My intuition was correct on the fact that the if statement in the complete: function(data) function needed to be inside brackets; otherwise, the 'wrong captcha' alert will always appear, no matter if the PHP reCaptcha check returns 'wrong' or not.

complete: function(data){
if (data.responseText == 'wrong') {
alert('Wrong captcha');
}
messageList.html(data.responseText);
updateShoutbox();
...

P.S. Can't get the codes to display correctly on this page... I'm trying to follow the formatting guidelines to the right but to no avail.

P.P.S. ReCaptcha is still returning always 'wrong'. Will update progress when there is some.

like image 662
user1247075 Avatar asked Nov 14 '22 09:11

user1247075


1 Answers

Once I have tried to load reCaptcha using AJAX. It didn't work. I have not tried too hard, as I could simply not use AJAX, and that's what I did. The same code that didn't work through AJAX, worked in a straight simple form.

Also, reCaptcha is not PHP-based, as you say. You can use any server-side language with it (PHP included).


I have looked into your index.php file. Before customizing the look & feel, let's try to do the basics work. I have seen a mix of iframe and script tags. All you need there is as simple as this (assuming your public key is the string starting with "6Ld_b8":

<input type="text" id="recaptcha_response_field" name="recaptcha_response_field" />
<script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k=6Ld_b84SAAAAAAv1Ds1vYHxQjyoowiCPkARtvB2W"></script>

Remove the iframe. You can remove anything else recaptcha-related, at the moment.

So, you have a form which will be posted to shoutbox.php. Then, just for a moment, add the code you pasted in your question at the beginning of this file. You need to include the recaptchalib.php, and you need to substitute "your_private_key" for your reCaptcha private key. So, the code will look like this:

// put this on top of shoutbox.php
include "recaptchalib.php";
$privatekey = "your_private_key"; // change this here to your reCaptcha private key!
$resp = recaptcha_check_answer ($privatekey, 
       $_SERVER["REMOTE_ADDR"],
       $_POST["recaptcha_challenge_field"],
       $_POST["recaptcha_response_field"]);
if (!$resp->is_valid) {
       echo '<b>WRONG WORDS!</b>';
} else {
       echo '<b>CORRECT WORDS! GREAT!</b>';
}
exit();

Now, you may want to try, and see what happens, and tell us.


EDIT 1

You just need to add the reCaptcha validation in the proper placement, inside shoutbox.php. It seems it can be in the "case insert" part, or inside the "insertMessage" function:

    case "insert":
        include "recaptchalib.php";
        $privatekey = "your_private_key"; // change this here to your reCaptcha private key!
        $resp = recaptcha_check_answer ($privatekey, 
            $_SERVER["REMOTE_ADDR"],
            $_POST["recaptcha_challenge_field"],
            $_POST["recaptcha_response_field"]);
        if ($resp->is_valid)
            echo insertMessage($_POST['nick'], $_POST['message']);
        break; 

Have you seen the complete: function(data){ lines in the javascript code? It is one of the $.ajax function call options. It specifies the function that will run after PHP code runs and sends its response back to the browser. The response from the server will be somewhere in the data parameter for you to use. If you want to display a message for the user warning him in case of "incorrect words" for the captcha, you need to:

  1. Return something from the server side (PHP code):

        if ($resp->is_valid)
            echo insertMessage($_POST['nick'], $_POST['message']);
        else
            echo 'wrong';
    
  2. Use this return in the client side (JS code):

    $.ajax({  
        type: "POST", url: "shoutbox.php", data: "action=insert&nick=" + nick + "&message=" + message,  
        complete: function(data){
            if (data.responseText == 'wrong')
                alert('Incorrect captcha words! Try again.');
            messageList.html(data.responseText);  
            updateShoutbox();  
            //reactivate the send button  
            $("#send").attr({ disabled:false, value:"Shout it!" });  
        }  
     });  
    

And yes, you will need to reload the reCaptcha (for the BACK button and in the case of a submit - successfull nor not). For the back button, you can set an "Expires" HTTP header using PHP or an HTML "meta" tag, forcing the page to reload. For the other case (after ajax form submission), you can request a new challenge by replacing the HTML using JavaScript/jQuery. Wrap the challenge in a div:

<div id="challenge">
    <input type="text" id="recaptcha_response_field" name="recaptcha_response_field" />
    <script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k=6Ld_b84SAAAAAAv1Ds1vYHxQjyoowiCPkARtvB2W"></script>
</div>

And "reload" it when necessary:

function reloadCaptcha(){
    $('#challenge').html('<input type="text" id="recaptcha_response_field" name="recaptcha_response_field" /><script type="text/javascript" src="http://www.google.com/recaptcha/api/challenge?k=6Ld_b84SAAAAAAv1Ds1vYHxQjyoowiCPkARtvB2W"></script>');
}
like image 76
J. Bruni Avatar answered Nov 16 '22 03:11

J. Bruni