Da ich ja nach wie vor der Meinung bin, dass ein Captcha für jedes Browsergame essentiell wichtig ist, das einen kompetativen Inhalt hat und nicht nur aus RPG und Zufall besteht, wenn man nicht totgebottet werden möchte, wollte ich hier mal einen kleinen Prototypen posten, wie ich es designen würde:
http://dicio.org:3000/captcha (ist nur mein Catalyst Testserver, also unperformant und wird regelmäßig neu gestartet wenn ich Code Edits einpflege, also net wundern)
Die Idee ist nicht neu, in der Umsetzung hab ich es aber noch nicht gesehen. Die Filter sind nur innerhalb von 20 Minuten zusammengeschustert und noch nicht gestackt, also nur einer je Bild. Die Datenbank ist mit 4x4 Bildern auch noch sehr gering, mit realen Daten wäre es wesentlich unwahrscheinlicher in einem View zweimal dasselbe Bild zu haben.
16:00 <*Kallisti> also ich hab versch. Kategorien von Bildern
16:00 <*Kallisti> in dem Fall nur 4 (Katzen, Hunde, Igel und Pandas)
16:00 <*Kallisti> jedes Bild ist 400x300 pixel groß
16:00 <*Kallisti> das script schneidet davon zufällig 320x240 aus -> randomness, vielleicht noch zu vergrößern
16:00 <*Kallisti> dann ist oben links das Original ohne filter
16:01 <*Kallisti> und über die drei andern (von denen immer eins aus derselben Kategorie ist wie das Original)
kommt 1 Filter
16:01 <*Kallisti> später würd ich da vielleicht noch filter kombinieren, die Werte ein wenig tunen und
wahrscheinlich Graustufen erzwingen
16:01 <*Kallisti> ist im Grunde simpelstes ImageMagick
16:01 <*Kallisti> Am Ende macht es aus den 4 Bildern eines
16:02 <*Kallisti> dann nur noch javascript das die Klickkoordinaten übermittelt und in der Session
speichern an welcher Posi das richtige Bild ist
Ich fürchte man kommt an Graustufen zumindest für die drei Auswahlbilder nicht vorbei, weil sonst ein Farbhistogramm kombiniert mit einem Sampledatensatz ein zu mächtiges Werkzeug wird. Das Ziel ist es ja, dem Angreifer nicht zu erlauben mit einer Beispieldatenbank die Menge der zu interpretierenden Eingabedaten endlich zu machen, sondern mit dynamischen Filtern etwas vorzutäuschen, das nur Menschen mit vertretbarem Aufwand analysieren können.
Danach loggt man einfach die Ergebnisse der Klicks. Normale Anwender dürften im Bereich 80-90% landen, ein Bot kommt eher auf 33-50%, sofern die Filter gut umgesetzt sind.
Denke das ist für die Userexperience weit angenehmer als die meisten anderen Captchas und gleichzeitig um einiges sicherer als der nicht geschlossene Kreis. :P
Code (würde man selbstverständlich später in Methoden gliedern und bischen schicker machen, ist ja nur nen prototype):
Code:
sub index :Path :Args(0) {
my ( $self, $c ) = @_;
my $path = "captcha";
my $stack = Image::Magick->new();
# the first pic's category
my $rand_cat = 1 + int(rand(4));
# which position will be of the same?
my $rand_hit = 1 + int(rand(3));
# load first picture
$stack->Read(($path."/src/".$rand_cat."-".(1+int(rand(4))).".jpg"));
# pick a random 320x240 slice from 400x300 pics
$stack->Crop(geometry => ('320x240+'.(int(rand(80))).'+'.(int(rand(60))).''));
$stack->Border(bordercolor => "lightgreen");
# 3 more to go
for(1..3) {
# this turns category
my $rand_turn;
# uh, this iteration is our hit, so display the right category
if($rand_hit == $_) {
$rand_turn = $rand_cat;
# a random hit, iterate until we get a category that does not match the target
} else {
do {
$rand_turn = 1 + int(rand(4));
} while ($rand_turn == $rand_cat);
}
# read the image, slice it
my $image = Image::Magick->new();
$image->Read(($path."/src/".$rand_turn."-".(1+int(rand(4))).".jpg"));
$image->Crop(geometry => ('320x240+'.(int(rand(80))).'+'.(int(rand(60))).''));
# apply some filters
my $effect = 1 + int(rand(10));
if($effect == 1) {
$image->Charcoal(radius => 3, sigma => 3);
} elsif ($effect == 2) {
$image->AddNoise(noise => 'Impulse');
} elsif ($effect == 3) {
$image->RadialBlur(angle => rand(20));
} elsif ($effect == 4) {
$image->Posterize(levels => 5);
} elsif ($effect == 5) {
$image->Sketch(radius => 3, sigma => 3, angle => 45);
} elsif ($effect == 6) {
$image->Spread(radius => 3);
} elsif ($effect == 7) {
$image->Solarize();
} elsif ($effect == 8) {
$image->Swirl(degrees => (80+rand(120)));
} elsif ($effect == 9) {
$image->Wave(amplitude => 6, wavelength => 60, interpolate => 'average');
} elsif ($effect == 10) {
$image->Wave(amplitude => 10, wavelength => 5, interpolate => 'average');
}
# apply more filters?
# runder Ausschnitt des Bildes in weisser Umrandung
#$image->Vignette();
#$image->Set(monochrome => 1);
#$image->Modulate(brightness => 100);
$image->Modulate(saturation => 80);
#$image->Modulate(hue => 100);
$image->Border(bordercolor => "grey");
# stack em all
push(@$stack, $image);
}
# put the 4 images together
$stack = $stack->Montage(geometry => '320x240+8+6', tile => '2x2', border => 2, shadow => 1);
$stack->Set(quality => 80);
# temporary file for output
my $th = File::Temp->new(DIR => "$path/temp", SUFFIX => ".jpg");
my $tname = $th->filename;
$stack->Write("jpg:$tname");
$c->response->content_type("image/jpg");
$c->response->body($th);
}