CAPTCHA für Codeigniter 4

Guten Tag! Trotz des Titels des Artikels werden die allgemeinen Methoden und Funktionen vorgestellt, mit denen ich mein Captcha erstellt habe. Diese können mit minimalen Änderungen in anderen Frameworks angewendet werden. Einige Funktionen und Ansätze basieren auf den Materialien des DIY CAPTCHA-Entwicklungspostens .

Einführung


Um mit Bildern arbeiten zu können, müssen Sie das Vorhandensein der GD-Bibliothek in PHP überprüfen. Dies kann mit der Funktion gd_info () erfolgen. In den vorgestellten Beispielen verwende ich Version 2.1.0 und PHP 7.4.3, was in diesem Fall nicht erforderlich ist, da die PHP7-Funktionen nicht verwendet werden.

Logik


Welches Captcha möchte ich sehen? Eine, die mir hilft, die Anzahl der Serveranforderungen während der Autorisierung auf meiner Site mit Codeigniter 4 zu reduzieren.

Zur Implementierung wird das Bild mit dem Code ausschließlich auf der Serverseite generiert, in einem temporären Ordner gespeichert, in base64 codiert und an den Benutzer zurückgegeben.

Entwicklung


Bevor wir das Bild zeichnen, schreiben wir die Codegenerierungsmethode.

public function generate_code() {
        srand((float) microtime() * 1000000);
        $chars = 'ABDEFHKNRSTYZabdefhknrstyz23456789'; //   
        $length = rand(5, 7); //  
        $numChars = strlen($chars); 
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[rand(0, $numChars - 1)];
        }
        $array_mix = preg_split('//', $str, -1, PREG_SPLIT_NO_EMPTY);
        shuffle($array_mix);
        delete_cookie('cap'); //  ,   ,      
        set_cookie('cap', md5(implode("", $array_mix)), self::$_code_time); //      md5   _code_time
        return implode("", $array_mix);
    }

Ich stelle fest, dass ich in Zukunft verschiedene Schriftarten zur Ausgabe von Zeichen verwenden werde. Sie müssen daher auf den ursprünglichen Zeichensatz oder auf die Schriftarten selbst achten, um Probleme mit Zeichen wie „Z“ und „z“, „X“ und „x“ zu vermeiden. "I" und "l" usw., da eine Verzerrung des Bildes die Captcha-Eingabe problematisch machen kann.

Ich erkläre die notwendigen Felder für die Zukunft.

public static $width = 220; //    
public static $height = 120; //  
public static $fonts_num = 4; //     /public/fonts/
private static $_code_time = 180; //     .

Ich bereite einige Methoden zum Erzeugen von Hintergründen und Rauschen vor (vollständige Auflistung am Ende).

/**
     *    .
     *
     * $mode == "parallel",      
     * $max —   
     */
private function _add_line($img, $mode = '', $max = 100) {
    for ($i = 0; $i < rand(0, $max); $i++) {
        $color = imagecolorallocate($img, rand(80, 150), rand(80, 150), rand(80, 150));
        if ($mode === 'parallel') {
            $r1 = rand(0, self::$width);
            $r2 = rand(0, self::$width);
            imageline($img, $r1, $r1, $r2, $r1, $color);
            imageline($img, $r1, $r2, $r1, rand(0, 220), $color);
        } else {
            imageline($img, rand(0, self::$width), rand(0, self::$width), rand(0, self::$width), rand(0, self::$width), $color);
        }
    }
}

private function _add_poly($img) { //   
    $points = [];
    for ($i = 0; $i < 10; $i++) {
        array_push($points, rand(0, self::$width * 2));
    }
    $color = imagecolorallocate($img, rand(80, 190), rand(80, 190), rand(80, 190));
    imageFilledPolygon($img, $points, 5, $color);
}

/**
     *    .
     *
     * $xn  $yn     .   ,   ,   .  ,     "".
     * $mode          ('normal').
     */
private function _set_glitch_color($image, $xn = 0, $yn = 0, $mode = 'normal') {
    $start = rand(self::$height / 2, self::$height / 2 - self::$height / 4);
    $finish = $start + rand(5, 15);
    for ($x = 0; $x < self::$width - 1; $x++) {
        for ($y = 0; $y < self::$height - 1; $y++) {
            if ($mode != 'normal') {
                $xn = rand(0, 1);
                $yn = rand(0, 1);
            } else {
                $finish = $start + 3;
            }
            if ($y > $start && $y < $finish) {
                imagesetpixel($image, $x + $xn, $y + $yn, imagecolorat($image, $x, $y));
            }
        }
    }
}

Fast fertig. Wir schreiben den Geheimcode in das Bild.

private function _add_text($img, $text) {
    $x = rand(10,20); //    X   .
    for ($i = 0; $i < strlen($text); $i++) {
        $text_color = imagecolorallocate($img, rand(150, 250), rand(150, 250), rand(150, 250));
        imagettftext($img, rand(35, 40), rand(0, 10) - rand(0, 10), $x, rand(55, 95), $text_color, 'fonts/' . rand(1, self::$fonts_num) . ".ttf", $text[$i]); //       1.ttf  *self::$fonts_num*.ttf
        $x += rand(25, 35);
    }
}

Wir kombinieren vorgefertigte Methoden, um ein Bild zu erstellen und den Code zu überprüfen.

public function img_code($code) {
    $image = imagecreatetruecolor(self::$width, self::$height);
    imageantialias($image, true);
    $rand_color = imagecolorallocate($image, rand(50, 120), rand(50, 120), rand(50, 120));
    imagefilledrectangle($image, 0, 0, self::$width, self::$height, $rand_color);
    $this->_add_rand_bg($image); //   
    $this->_add_text($image, $code); 
    $this->_add_glitch($image, 'normal');
    $this->_add_glitch($image, 'boom');
    $this->_add_line($image, 'rand', 200); //    
    $file = 'temp/' . md5($code) . ".png"; 
    imagepng($image, $file); //  
    imagedestroy($image);
    $res = base64_encode(file_get_contents($file)); //    base64()
    unlink($file); //  
    return $res;
}

public function check($tested) {
    $cap = get_cookie('cap');
    $r['error'] = '';
    if (!$cap) { //   
        $r['error'] = '    .';
    } elseif (strcmp($tested, $cap)) {
        $r['error'] = '   .';
    }
    delete_cookie('cap'); //     
    return $r;
}

Verwenden von


Bereiten Sie das Captcha in den erforderlichen Controllern wie folgt vor:

public function __construct() {
    ...
    $this->captcha = new \App\Libraries\Captcha();
    ...
}

public function some_function() {
    ...
    $data['captcha'] = $this->captcha->img_code( $this->captcha->generate_code() );
    return view('page/template', $data);
}

public function recaptcha(){ //  ajax-
    return $this->captcha->img_code( $this->captcha->generate_code() );
}

Erstellen Sie in der Vorlage ein Token und eine Schaltfläche zum erneuten Generieren des Bildes:

...
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" id="csrf"/>
...
<img src="data:image/png;base64,<?= $captcha ?>" id="cap" width="220" height="120"/>
<div id="ref" onclick="recaptcha()">⥁</div>
...

Wir fügen eine Route für eine Ajax-Anfrage hinzu.

$routes->post('/recap', 'AuthController::recaptcha');

Und Ajax selbst.

var numlog = 0;
function recaptcha() {
    if(numlog <= 5){
        $.ajax({
            type: 'post',
            url: '/recap',
            data: {csrf_token: $('#csrf').val()},
            success: function (result) {
                numlog++;
                $('#cap').attr('src', "data:image/png;base64," + result + '');
            }
        });
    }else{
        $('#ref').css('display', 'none');
    }
}

Gesamt


Eine verrauschte Farbpalette und eine Reihe von Verzerrungen mit unterschiedlichen Parametern erschweren die Lösung von Captcha erheblich. Nach dem Spielen mit den Konstanten können Sie unterschiedliche Ergebnisse erzielen.

Ich habe alle Ergebnisse in einem grafischen Editor überprüft, indem ich einen Schwellenwert auf die Bilder gesetzt habe, um die Zeichen hervorzuheben, die für die Erkennung verwendet werden können.



Rauschen und zufällige Linien erhöhen natürlich die Komplexität. Ich komme jedoch zu folgendem Schluss: Falls erforderlich und gewünscht, ist diese Lösung nicht vollständig sicher, hilft jedoch beim Schutz vor normalen Bots.

Der vollständige Code kann auf dem Github angezeigt werden . Ich freue mich über Empfehlungen und Kommentare.

All Articles