1 I don't necessarily trust all OpenID providers to stop bots. I note that ikiwiki allows [[banned_users]], and that there are other todos such as [[todo/openid_user_filtering]] that would extend this. However, it might be nice to have a CAPTCHA system.
3 I imagine a plugin that modifies the login screen to use <http://recaptcha.net/>. You would then be required to fill in the captcha as well as log in in the normal way.
5 > I hate CAPTCHAs with a passion. Someone else is welcome to write such a
8 > If spam via openid (which I have never ever seen yet) becomes
9 > a problem, a provider whitelist/blacklist seems like a much nicer
10 > solution than a CAPTCHA. --[[Joey]]
12 >> Apparently there has been openid spam (you can google for it). But as for
13 >> white/black lists, were you thinking of listing the openids, or the content?
14 >> Something like the moinmoin global <http://master.moinmo.in/BadContent>
17 Okie - I have a first pass of this. There are still some issues.
19 Currently the code verifies the CAPTCHA. If you get it right then you're fine.
20 If you get the CAPTCHA wrong then the current code tells formbuilder that
21 one of the fields is invalid. This stops the login from going through.
22 Unfortunately, formbuilder is caching this validity somewhere, and I haven't
23 found a way around that yet. This means that if you get the CAPTCHA
24 wrong, it will continue to fail. You need to load the login page again so
25 it doesn't have the error message on the screen, then it'll work again.
27 > fixed this - updated code is attached.
29 A second issue is that the OpenID login system resets the 'required' flags
30 of all the other fields, so using OpenID will cause the CAPTCHA to be
33 > This is still not fixed. I would have thought the following patch would
34 > have fixed this second issue, but it doesn't.
36 --- a/IkiWiki/Plugin/openid.pm
37 +++ b/IkiWiki/Plugin/openid.pm
38 @@ -61,6 +61,7 @@ sub formbuilder_setup (@) { #{{{
39 # Skip all other required fields in this case.
40 foreach my $field ($form->field) {
41 next if $field eq "openid_url";
42 + next if $field eq "recaptcha";
43 $form->field(name => $field, required => 0,
51 You need to go to <http://recaptcha.net/api/getkey> and get a key set.
52 The keys are added as options.
54 reCaptchaPubKey => "LONGPUBLICKEYSTRING",
55 reCaptchaPrivKey => "LONGPRIVATEKEYSTRING",
57 You can also use "signInSSL" if you're using ssl for your login screen.
60 The following code is just inline. It will probably not display correctly, and you should just grab it from the page source.
65 # Ikiwiki password authentication.
66 package IkiWiki::Plugin::recaptcha;
73 hook(type => "formbuilder_setup", id => "recaptcha", call => \&formbuilder_setup);
77 eval q{use Getopt::Long};
79 Getopt::Long::Configure('pass_through');
80 GetOptions("reCaptchaPubKey=s" => \$config{reCaptchaPubKey});
81 GetOptions("reCaptchaPrivKey=s" => \$config{reCaptchaPrivKey});
84 sub formbuilder_setup (@) { #{{{
87 my $form=$params{form};
88 my $session=$params{session};
90 my $pubkey=$config{reCaptchaPubKey};
91 my $privkey=$config{reCaptchaPrivKey};
92 debug("Unknown Public Key. To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey")
93 unless defined $config{reCaptchaPubKey};
94 debug("Unknown Private Key. To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey")
95 unless defined $config{reCaptchaPrivKey};
96 my $tagtextPlain=<<EOTAG;
97 <script type="text/javascript"
98 src="http://api.recaptcha.net/challenge?k=$pubkey">
102 <iframe src="http://api.recaptcha.net/noscript?k=$pubkey"
103 height="300" width="500" frameborder="0"></iframe><br>
104 <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
105 <input type="hidden" name="recaptcha_response_field"
106 value="manual_challenge">
110 my $tagtextSSL=<<EOTAGS;
111 <script type="text/javascript"
112 src="https://api-secure.recaptcha.net/challenge?k=$pubkey">
116 <iframe src="https://api-secure.recaptcha.net/noscript?k=$pubkey"
117 height="300" width="500" frameborder="0"></iframe><br>
118 <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
119 <input type="hidden" name="recaptcha_response_field"
120 value="manual_challenge">
126 if ($config{signInSSL}) {
127 $tagtext = $tagtextSSL;
129 $tagtext = $tagtextPlain;
132 if ($form->title eq "signin") {
133 # Give up if module is unavailable to avoid
134 # needing to depend on it.
135 eval q{use LWP::UserAgent};
137 debug("unable to load LWP::UserAgent, not enabling reCaptcha");
141 die("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey")
143 die("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey")
145 die("To use reCAPTCHA you must know the remote IP address")
146 unless $session->remote_addr();
154 message => "CAPTCHA verification failed",
157 # validate the captcha.
158 if ($form->submitted && $form->submitted eq "Login" &&
159 defined $form->cgi_param("recaptcha_challenge_field") &&
160 length $form->cgi_param("recaptcha_challenge_field") &&
161 defined $form->cgi_param("recaptcha_response_field") &&
162 length $form->cgi_param("recaptcha_response_field")) {
164 my $challenge = "invalid";
165 my $response = "invalid";
166 my $result = { is_valid => 0, error => 'recaptcha-not-tested' };
168 $form->field(name => "recaptcha",
169 message => "CAPTCHA verification failed",
172 if ($challenge ne $form->cgi_param("recaptcha_challenge_field") or
173 $response ne $form->cgi_param("recaptcha_response_field")) {
174 $challenge = $form->cgi_param("recaptcha_challenge_field");
175 $response = $form->cgi_param("recaptcha_response_field");
176 debug("Validating: ".$challenge." ".$response);
177 $result = check_answer($privkey,
178 $session->remote_addr(),
179 $challenge, $response);
181 debug("re-Validating");
184 if ($result->{is_valid}) {
196 # The following function is borrowed from
197 # Captcha::reCAPTCHA by Andy Armstrong and are under the PERL Artistic License
200 my ( $privkey, $remoteip, $challenge, $response ) = @_;
203 "To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey"
206 die "For security reasons, you must pass the remote ip to reCAPTCHA"
209 if (! ($challenge && $response)) {
210 debug("Challenge or response not set!");
211 return { is_valid => 0, error => 'incorrect-captcha-sol' };
214 my $ua = LWP::UserAgent->new();
216 my $resp = $ua->post(
217 'http://api-verify.recaptcha.net/verify',
219 privatekey => $privkey,
220 remoteip => $remoteip,
221 challenge => $challenge,
222 response => $response
226 if ( $resp->is_success ) {
227 my ( $answer, $message ) = split( /\n/, $resp->content, 2 );
228 if ( $answer =~ /true/ ) {
229 debug("CAPTCHA valid");
230 return { is_valid => 1 };
234 debug("CAPTCHA failed: ".$message);
235 return { is_valid => 0, error => $message };
239 debug("Unable to contact reCaptcha verification host!");
240 return { is_valid => 0, error => 'recaptcha-not-reachable' };