Captcha (Features)

by Martin66 ⌂ @, Saturday, February 06, 2016, 21:06 (3001 days ago)

Hallo Entwickler,

großes LOB für das Feature, einzelne IPs zu sperren! Mein Logfile zeigt mir, dass es ein Spammer bei mir nun schon seit Monaten erfolglos versucht. :lol:

Es handelt sich um einen Brute-Force-Angriff, ca. alle 1-2 Minuten. Das Captcha scheint dabei kein Problem zu sein. Deswegen habe ich mir über das Captcha Gedanken gemacht, da ich mein Forum auch weiterhin für unregistrierte User offen halten will:

Ein 5-stelliges Captcha ist für einen Brute-Force-Angriff kein Hindernis. Außerdem ist der Zeichenvorrat derzeit auf 28 Zeichen begrenzt: 28^5 (17 Millionen) Möglichkeiten. Und auch Zeichenerkennung ist einfach: Wenn es eine bestimmte Anzahl fixer Hintergrundbilder gibt, muss man nur mehrfach ein Captcha aufrufen, die Bilder vergleichen, und kann dann den Hintergrund rausrechnen. Deswegen sollte der Hintergrund variabel sein.

Daher habe ich modules/captcha/captcha.php überarbeitet. Jetzt wird ein mal 7-, mal 8-stelliger Code benötigt, und der Zeichenvorrat ist um @%&?# ergänzt. Daraus ergeben sich 33^7 (42.6 Milliarden) bzw. 33^8 (1.4 Billionen) verschiedene Eingabemöglichkeiten. Brute force wird so wohl verdammt schwer. Der Hintergrund wird jedesmal neu erstellt, ohne vorgegebene Bilder, und da man nicht mal weiß, ob 7 oder 8 Zeichen benötigt werden, ist auch das Auslesen aus der Grafik erschwert.

Ich habe keine Ahnung, ob das Ganze was bringt :-D Aber dies als Vorschlag für die nächste mlf-Version. Und da es das erste Mal ist, dass ich etwas mit PHP gemacht habe, würden mich Meinungen und Verbesserungsvorschläge freuen :-) Das Ergebnis kann man unter http://www.access-tutorial.de/forum bewundern.

Meine Änderungen:

modules/captcha/captcha_image.php
---------------------------------
Zeile 8:

  $captcha->generate_image($_SESSION['captcha_session'],'fonts/');


Das backgrounds-Verzeichnis wird obsolet

lang/*.lang
-----------
Da jetzt auch ein paar Sonderzeichen vorkommen können, sollte captcha_expl_image angepasst werden (german.lang: "Bitte die Zeichenfolge des Bildes eingeben:")

modules/captcha/captcha.php
---------------------------

<?php
/*******************************************************************************
* PHP CAPTCHA class (C) 2008 alex@mylittlehomepage.net                         *
* http://mylittlehomepage.net/                                                 *
* rewritten 02/2016 by Martin Asal (asal@gmx.de)                               *
*******************************************************************************/
 
/*******************************************************************************
* This program is free software: you can redistribute it and/or modify         *
* it under the terms of the GNU General Public License as published by         *
* the Free Software Foundation, either version 3 of the License, or            *
* (at your option) any later version.                                          *
*                                                                              *
* This program is distributed in the hope that it will be useful,              *
* but WITHOUT ANY WARRANTY; without even the implied warranty of               *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                *
* GNU General Public License for more details.                                 *
*                                                                              *
* You should have received a copy of the GNU General Public License            *
* along with this program.  If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************/
 
class Captcha
{
function check_captcha($code,$entered_code)
{
 if(strtolower($entered_code) == strtolower($code)) return true;
 else return false;
}
 
function generate_code($letters='abcdefhjkmnpqrstuvwxyz234568@%&?#')
{
 mt_srand((double)microtime()*1000000);
 $clength=mt_rand(7,8);
 $code='';
 for($i=0;$i<$clength;$i++)
 {
  $code.=substr($letters,mt_rand(0,strlen($letters)-1),1);
 }
 return $code;
}
 
function generate_image($code,$fonts_folder='')
{
 $code_length = strlen($code);
 $font_size = 23;
 $font_pos_x = 8 + 36*(8-$code_length)/2;    // centers the code
 $font_pos_y = 35;
 
 // get fonts:
 if($fonts_folder!='')
 {
  $handle=opendir($fonts_folder);
  while($file = readdir($handle))
  {
   if(preg_match('/\.ttf$/i', $file)) $fonts[] = $file;
  }
  closedir($handle);
 }
 
 $im = ImageCreate(290, 50);
 
 // create a color palette. There's a maximum of 255 different colors
 $bgRects = 15;
 $bgGraffity = 100;
 for($i=0; $i<=255; $i++)
 {
  switch($i){
  case 0:        // Primary background color
   $col[$i] = ImageColorAllocate ($im, mt_rand(192,255), mt_rand(192,255), mt_rand(192,255));
   break;
  case($i<=$bgRects):      // colors for background rectangles
   $col[$i] = ImageColorAllocate ($im, mt_rand(128,255), mt_rand(128,255), mt_rand(128,255));
   break;
  case($i<=$bgRects+$bgGraffity):     // colors for background graffities
   $col[$i] = ImageColorAllocate ($im, mt_rand(160,255), mt_rand(160,255), mt_rand(160,255));
   break;
  case($i<=$bgRects+$bgGraffity+$code_length):   // colors for text
   $col[$i] = ImageColorAllocateAlpha ($im, mt_rand(0,96), mt_rand(0,96), mt_rand(0,96), 16);
   break;
  default:       // Colors for dots (I call it "salt")
   $col[$i] = ImageColorAllocate ($im, mt_rand(0,255), mt_rand(0,255), mt_rand(0,255));
  }
 }
 
 // create background
 imageFilledRectangle ( $im, 0,0, 290,50, $col[0] );
 for($i=1;$i<=$bgRects;$i++)      // background Rects
 {
  $x1 = mt_rand(0,290);
  $y1 = mt_rand(0,50);
  $x2 = $x1 + mt_rand(-150,150);
  $y2 = $y2 + mt_rand(-25,25);
  imageFilledRectangle ( $im, $x1,$y1, $x2,$y2, $col[$i] );
 }
 for($i=$bgRects+1; $i<=$bgGraffity; $i++)    // background Arcs
 {
  $x = mt_rand(10,280);
  $y = mt_rand(10,40);
  $w = mt_rand(0,40);
  $h = mt_rand(0,40);
  $s = mt_rand(0,180);
  $e = $s + mt_rand(0,180);
  imageSetThickness($im, mt_rand(1,3));
  $Punkt = imageArc ($im, $x,$y, $w,$h, $s,$e, $col[$i] );
 }
 
 // use fonts, if available:
 if(isset($fonts))
 {
  for($i=0;$i<$code_length;$i++)
  {
   $angle = mt_rand(-25,25);
   $text_color = $col[$bgRects + $bgGraffity + $i + 1];
   ImageTTFText($im, $font_size+mt_rand(-3,4), $angle, $font_pos_x, $font_pos_y, $text_color, $fonts_folder.$fonts[mt_rand(0,count($fonts)-1)], substr($code,$i,1));
   $font_pos_x=$font_pos_x+($font_size+13);
  }
 }
 // if not, use internal font:
 else
 {
  // set text color:
  $x = 50+mt_rand(0,50);
  $y = 10+mt_rand(0,10);
  $text_color = $col[$bgRects + $bgGraffity + 1];
  imageFilledRectangle ( $im, $x-5,$y, $x+80,$y+15, $col[0] );
  ImageString($im, 5, $x, $y, $code, $text_color);
 }
 
 // some "salt" above all
 for($i=0;$i<600;$i++)
 {
  $x = mt_rand(0,290);
  $y = mt_rand(0,50);
  $Punkt = imageSetPixel ($im, $x,$y, $col[mt_rand(0,255)] );
 }
        if(mt_rand(0,1))
        {
                imageFilter($im, IMG_FILTER_NEGATE);
        }
 
 header("Expires: Expires: Mon, 1 Feb 2016 00:00:00 GMT");
 header("Cache-Control: max-age=0");
 header("Content-type: image/png");
 ImagePNG($im);
 exit();
}
 
function generate_dummy_image()
{
 $im = @ImageCreate(180, 40);
 $background_color = ImageColorAllocate ($im, 234, 234, 234);
 $text_color = ImageColorAllocate ($im, 0, 0, 0);
 #ImageString($im, 3, 7, 4, 'CAPTCHA not available', $text_color);
 header("Expires: Expires: Sat, 20 Oct 2007 00:00:00 GMT");
 header("Cache-Control: max-age=0");
 header("Content-type: image/png");
 ImagePNG($im);
}
 
// for math CAPTCHA:
function generate_math_captcha($number1from=1,$number1to=10,$number2from=0,$number2to=10)
{
 $number[0] = rand($number1from,$number1to);
 $number[1] = rand($number2from,$number2to);
 $number[2] = $number[0] + $number[1];
 return $number;
}
 
function check_math_captcha($result, $entered_result)
{
 if(intval($result) == intval($entered_result)) return true;
 else return false;
}
}
?>
 

Martin


Complete thread:

 RSS Feed of thread