Using reCAPTCHA with HTML forms to CGI Perl (long)

385 views
Skip to first unread message

airBiscuit

unread,
Jul 25, 2007, 3:49:26 PM7/25/07
to reCAPTCHA
It took a couple of mornings to sort out how to get reCAPTCHA to work
with an existing set of CGI-based forms. I'm sure many of us are
comfortable with using HTML forms calling Perl CGI scripts. We
probably also have a pretty strong install base of these utilities,
and moving over to something like PHP or embedding our html forms into
Perl itself is not an acceptable option.

Therefore, we want a solution where reCAPTCHA becomes a component of
the form and determines whether the perl script called by the form
will accept the entry or throw it away, giving an error message.

I came to this list and no answers were presented, though I did take
note that the reCAPTCHA developer was working on a self-contained
solution.

The following description is for those comfortable with perl and who
can deal with installing perl modules and debugging their code.

First, you will need to consult the CPAN site for any perl modules you
need. First, you need Captcha::reCAPTCHA. This is provided as a
tar.gz download. If you dig in there, you will find that there is a
reCAPTCHA.pm file. That's the module you want. The quick and dirty
way to install these is to copy the .pm file to wherever your
libraries are located, like /usr/perl/lib/5.8.5/ and so forth. Let's
just use that directory for argument.

Because reCAPTCHA is part of the package name Captcha, it needs to be
placed inside a directory named 'Captcha'. So create a directory
'Captcha' in /usr/perl/lib/5.8.5 and then copy reCAPTCHA.pm into /usr/
perl/lib/5.8.5/Captcha.

If you try to run your script, and it reports an error saying it
cannot find reCAPTCHA.pm, look at the @INC listings in the error
message, which is where perl searches for .pm files. If it doesn't
include the directory where Captcha is, then you need to add it to the
list. For my example, you'd enter before all of the 'use' commands:

use lib ('/usr/perl/lib/5.8.5/');

This adds the entry to the @INC list.

What you will probably run into is a search for other .pm files. These
would be LWP::UserAgent, HTML::Tiny, etc. Do these the same way as
you would do the reCAPTCHA.pm example above. LWP is the package name
for UserAgent.pm, so you need a directory named LWP. If you are
missing a pm file like 'version', then you'll have a version.pm that
doesn't go into a package directory, but you will have some
additional .pm files in the download that need to going into a
directory named version. The error messages will tell you what it's
looking for. You just have to go to CPAN to find them. You might
have to do a 'use lib' entry inside of reCAPTCHA.pm if your
dependencies are not located where perl expects them. Just modify the
file so that it works.

The easiest way to tell if your CGI perl script is going to work is to
try to run it on the command line. Keep up this process until it runs
without errors. Just make sure that you block off execution of
database writes, email sending, and other operations. You can
shortcut your script by putting an 'exit 0' somewhere prematurely in
your script to halt it from doing your critical operations while you
test.

Okay, assuming that you have reCAPTCHA in your CGI perl script (use
Captcha::reCAPTCHA;), you now need to carry out the entry capture
operations in the perl script itself (we'll get to the HTML form page
later).

The following example illustrates what should probably be the first
thing checked on your form, the reCAPTCHA check:

------
use Captcha::reCAPTCHA;
&ReadParse(*in); # this is using a cgi lib that assigns form entries
to '%in'

print &PrintHeader(); # make sure there is a HTTP header printed

# Place reCAPTCHA Checking here
$private_key = "xxxThis_is_the_private_keyxxx";
$cap = Captcha::reCAPTCHA->new;
$cap_result = $cap->check_answer($private_key,
$ENV{'REMOTE_ADDR'},
$in{recaptcha_challenge_field},
$in{recaptcha_response_field});
if (! $cap_result->{is_valid}) {
print "I'm sorry, but your entry is invalid. ($cap_result->{error})
\n";
exit 0;
}
# otherwise, the entry is good, continue processing

# End reCAPTCHA Checking

-----

So now, assuming this script doesn't have any errors in it, all you
need to do now is go back to your HTML form and insert the following
entries where you want the reCAPTCHA to appear:

------

<!--INSERT CAPTCHA CHECK HERE -->
<p>
Please enter the two words you see into the yellow box before you
Submit your fo
rm.
</p><p>

<script type="text/javascript"
src="http://api.recaptcha.net/challenge?
k=xxxThis_is_the_public_keyxxx">
</script>

<noscript>
<iframe src="http://api.recaptcha.net/noscript?
k=xxxThis_is_the_public_keyxxx"
height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40">
</textarea>
<input type="hidden" name="recaptcha_response_field"
value="manual_challenge">
</noscript>

</p>
<!--END CAPTCHA CHECK-->


------

That's pretty much it. If your public key on the HTML form is not
your assigned key, or the private key in the perl script is not the
proper assigned counterpart, you'll get an error message back from
recaptcha.net telling you that you need the proper key. Also, make
sure that you are actually passing the key values and not just empty
fields because you will get the same message when that happens.

So, to recap what we did:


Use the HTML script and noscript representations of the reCAPTCHA
call, as found in the reCAPTCHA documentation. Just make it a part of
your form.

Your HTML form will contain the PUBLIC KEY.

Your perl script will contain the PRIVATE KEY.

You will be trapping two new variables submitted by the form into your
CGI script. They are recaptcha_challenge_field and
recaptcha_response_field. Remember that these
are typically referenced by perl CGI libraries as hash keys (i.e.
$in{recaptcha_response_field}).

You make the call to check_answer using a new instance of
Captcha::reCAPTCHA. It fortunately doesn't have to be an identical
instance to what was on the web page, like the php example shows.
What you get back is a handle to a hash ($result). There are two keys
in this hash returned: is_valid and error. If is_valid is 0, then
the challenge failed. If it is 1, then the correct entry was made and
you can accept the form entry. Using error is optional, and it
returns an error code name if is_valid is 0.

Reply all
Reply to author
Forward
0 new messages