X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/blobdiff_plain/6d402b5a9ae7a46504015ca58ff8f8f2761e8062..899c319191c8f82b03f7d67f3feeeae437423e2f:/doc/todo/require_CAPTCHA_to_edit.mdwn?ds=sidebyside diff --git a/doc/todo/require_CAPTCHA_to_edit.mdwn b/doc/todo/require_CAPTCHA_to_edit.mdwn index ec70d2a83..0e32afc65 100644 --- a/doc/todo/require_CAPTCHA_to_edit.mdwn +++ b/doc/todo/require_CAPTCHA_to_edit.mdwn @@ -2,3 +2,231 @@ I don't necessarily trust all OpenID providers to stop bots. I note that ikiwik I imagine a plugin that modifies the login screen to use . You would then be required to fill in the captcha as well as log in in the normal way. +> I hate CAPTCHAs with a passion. Someone else is welcome to write such a +> plugin. +> +> If spam via openid (which I have never ever seen yet) becomes +> a problem, a provider whitelist/blacklist seems like a much nicer +> solution than a CAPTCHA. --[[Joey]] + +>> Apparently there has been openid spam (you can google for it). But as for +>> white/black lists, were you thinking of listing the openids, or the content? +>> Something like the moinmoin global +>> list? + +Okie - I have a first pass of this. There are still some issues. + +Currently the code verifies the CAPTCHA. If you get it right then you're fine. +If you get the CAPTCHA wrong then the current code tells formbuilder that +one of the fields is invalid. This stops the login from going through. +Unfortunately, formbuilder is caching this validity somewhere, and I haven't +found a way around that yet. This means that if you get the CAPTCHA +wrong, it will continue to fail. You need to load the login page again so +it doesn't have the error message on the screen, then it'll work again. + +> fixed this - updated code is attached. + +A second issue is that the OpenID login system resets the 'required' flags +of all the other fields, so using OpenID will cause the CAPTCHA to be +ignored. + +> This is still a todo. + +Instructions +===== + +You need to go to and get a key set. +The keys are added as options. + + reCaptchaPubKey => "LONGPUBLICKEYSTRING", + reCaptchaPrivKey => "LONGPRIVATEKEYSTRING", + +You can also use "signInSSL" if you're using ssl for your login screen. + + +The following code is just inline. It will probably not display correctly, and you should just grab it from the page source. + +---------- + +#!/usr/bin/perl +# Ikiwiki password authentication. +package IkiWiki::Plugin::recaptcha; + +use warnings; +use strict; +use IkiWiki 2.00; + +sub import { #{{{ + hook(type => "formbuilder_setup", id => "recaptcha", call => \&formbuilder_setup); +} # }}} + +sub getopt () { #{{{ + eval q{use Getopt::Long}; + error($@) if $@; + Getopt::Long::Configure('pass_through'); + GetOptions("reCaptchaPubKey=s" => \$config{reCaptchaPubKey}); + GetOptions("reCaptchaPrivKey=s" => \$config{reCaptchaPrivKey}); +} #}}} + +sub formbuilder_setup (@) { #{{{ + my %params=@_; + + my $form=$params{form}; + my $session=$params{session}; + my $cgi=$params{cgi}; + my $pubkey=$config{reCaptchaPubKey}; + my $privkey=$config{reCaptchaPrivKey}; + debug("Unknown Public Key. To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey") + unless defined $config{reCaptchaPubKey}; + debug("Unknown Private Key. To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey") + unless defined $config{reCaptchaPrivKey}; + my $tagtextPlain=< + + + +EOTAG + + my $tagtextSSL=< + + + +EOTAGS + + my $tagtext; + + if ($config{signInSSL}) { + $tagtext = $tagtextSSL; + } else { + $tagtext = $tagtextPlain; + } + + if ($form->title eq "signin") { + # Give up if module is unavailable to avoid + # needing to depend on it. + eval q{use LWP::UserAgent}; + if ($@) { + debug("unable to load LWP::UserAgent, not enabling reCaptcha"); + return; + } + + die("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey") + unless $pubkey; + die("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey") + unless $privkey; + die("To use reCAPTCHA you must know the remote IP address") + unless $session->remote_addr(); + + $form->field( + name => "recaptcha", + label => "", + type => 'static', + comment => $tagtext, + required => 1, + message => "CAPTCHA verification failed", + ); + + # validate the captcha. + if ($form->submitted && $form->submitted eq "Login" && + defined $form->cgi_param("recaptcha_challenge_field") && + length $form->cgi_param("recaptcha_challenge_field") && + defined $form->cgi_param("recaptcha_response_field") && + length $form->cgi_param("recaptcha_response_field")) { + + my $challenge = "invalid"; + my $response = "invalid"; + my $result = { is_valid => 0, error => 'recaptcha-not-tested' }; + + $form->field(name => "recaptcha", + message => "CAPTCHA verification failed", + required => 1, + validate => sub { + if ($challenge ne $form->cgi_param("recaptcha_challenge_field") or + $response ne $form->cgi_param("recaptcha_response_field")) { + $challenge = $form->cgi_param("recaptcha_challenge_field"); + $response = $form->cgi_param("recaptcha_response_field"); + debug("Validating: ".$challenge." ".$response); + $result = check_answer($privkey, + $session->remote_addr(), + $challenge, $response); + } else { + debug("re-Validating"); + } + + if ($result->{is_valid}) { + debug("valid"); + return 1; + } else { + debug("invalid"); + return 0; + } + }); + } + } +} # }}} + +# The following function is borrowed from +# Captcha::reCAPTCHA by Andy Armstrong and are under the PERL Artistic License + +sub check_answer { + my ( $privkey, $remoteip, $challenge, $response ) = @_; + + die + "To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey" + unless $privkey; + + die "For security reasons, you must pass the remote ip to reCAPTCHA" + unless $remoteip; + + if (! ($challenge && $response)) { + debug("Challenge or response not set!"); + return { is_valid => 0, error => 'incorrect-captcha-sol' }; + } + + my $ua = LWP::UserAgent->new(); + + my $resp = $ua->post( + 'http://api-verify.recaptcha.net/verify', + { + privatekey => $privkey, + remoteip => $remoteip, + challenge => $challenge, + response => $response + } + ); + + if ( $resp->is_success ) { + my ( $answer, $message ) = split( /\n/, $resp->content, 2 ); + if ( $answer =~ /true/ ) { + debug("CAPTCHA valid"); + return { is_valid => 1 }; + } + else { + chomp $message; + debug("CAPTCHA failed: ".$message); + return { is_valid => 0, error => $message }; + } + } + else { + debug("Unable to contact reCaptcha verification host!"); + return { is_valid => 0, error => 'recaptcha-not-reachable' }; + } +} + +1; +