Táto stránka pre svoju správnu funkčnosť vyžaduje súbory cookies. Slúžia na authentifikáciu návštevníka, analýzu návštevnosti a reklamnú personalizáciu.
logo
Prihlásenie / Registrácia
mobile

Zavrieť
 

Jednoduchá CAPTCHA v PHP

V tomto článku si povieme niečo o CAPTCHA kódoch a vytvoríme si ich jednoduchých generátor.
Každý z nás sa už niekoľko krát v živote stretol s tým, že nejaká stránka od neho vyžadovala opísanie nejakého zdeformovaného textu z obrázku. Zaujímavosťou je fakt, ze množstvo ľudí ani netuší, „čo je to za výmysel" a na čo je to dobré. Už som sa stretol aj s názorom, že sa jedná o test na prítomnosť detí na stránke, resp. aby sa im znemožnilo registrovať sa na stránke.

Reálny význam CAPTCHA kódov je samozrejme iný. Samotný výraz CAPTCHA je skratkou pre "Completely Automated Public Turing test to tell Computers and Humans Apart", čo by sa dalo preložiť ako plne aický Turingov test na rozpoznanie počítačov a ľudí. Inak povedané tvorcovia stránok sa týmto spôsobom snažia zistiť, či je na stránke reálny človek alebo počítač. Práve táto informácia je dôležitá pri ochrane stránky pred spamom a prípadnými inými nežiadúcimi aizovanými akciami na stránke (napr. aické sťahovanie súborov zo stránky bez potreby navštíviť stránku a pod).

Technické prevedenia CAPTCHA sa v priebehu rokov menilo a prispôsobovalo aktuálnym možnostiam ich obchádzania. Od jednoduchých textov v obrázku po zdeformované písmena, cez matematické úlohy v obrázku s nutnosťou zadať ich výsledok, po čisto obrázkové riešenia. V súčastnosti existuje množstvo viac či menej sofistikovaných riešení implementácie CAPTCHA.

Dôležité je si ale uvedomiť, že na náš web môžu pristupovať aj ľudia s nejakou formou zrakového postihnutia, preto prílišné komolenie alebo maskovanie v CAPTCHA kóde môže byť kontraproduktívne.

Pri tvorbe webu máme v princípe dve možnosti. Môžeme použiť riešenie tretích strán (ako napríklad reCAPTCHA od Google), alebo sa pokúsiť o implementáciu CAPTCHA vo vlastnej réžii. V tomto článku sa zameriame na druhú zmienenú možnosť a ukážeme si jednoduché riešenie CAPTCHA.

Popis riešenia
Za použitia prostriedkov, ktoré nám ponúka PHP si vytvoríme generátor CAPTCHA obrázkov, ktoré následné zobrazíme v HTML formulári. Na zmätenie jednoduchších SPAMBot-ov bude obrázok obsahovať niekoľko falošných textov, ktoré skutočnému návštevníkovi stránky vizuálne odfiltrujeme pomocou CSS. V prípade, ak SAPMBot dokáže parsovať CSS na dostatočnej úrovni, aby nám na tento fígeľ prišiel, musíme vymyslieť aj niečo iné. Okrem samotného textu ktorý má užívateľ zadať bude obrázok obsahovať aj menej výrazné texty v pozadí, by opäť mohli SPAMBota potrápiť. A aby to nemal také jednoduché, tak na texty v obrázku použijeme menej obvyklý font písma.

Technické riešenie
Ako bolo už spomenuté, na vytvorenie tohto riešenia budeme použijeme PHP, ktoré však pre svoj beh potrebuje WEB server s PHP interpretom, preto ak nemáte takýto server k dispozícii odporúčam pred pokračovaním v tomto článku vyhľadať na internete informácie o tom ako si ho nainštalovať.

Pre naše riešenie si vytvoríme nový priečinok, do ktorého vytvoríme prázdne súbory index.php, captcha.php a captchagen.php. Okrem samotných php súborov potrebujeme aj ttf font, ktorým budú texty v obrázku napísane. Prejdene teda na nejaký vyhľadávač fontov na internete (napríklad 1001freefonts.com, fonts2u.com) a nejaký si zaobstaráme. Pokúste sa vybrať font, ktorý nebude príliš nečitateľný aj pre človeka, ale ani príliš jednoduchý. V tomto texte budem používať konkrétne font „Cut Outs“ zo stránky 1001freefonts.com. Vybraný súbor ttf rozbalíme (zvyčajne býva zabalený v archíve spolu s informáciami, prípadne licenciou) a pod názvom captcha.ttf ho vložíme do priečinka projektu.

Otvoríme si súbor captchagen.php, a vytvoríme si v ňom prázdnu triedu CaptchaGen.

Zdrojový kód:
<?php
class CaptchaGen {

}
?>

Táto trieda bude obsahovať 2 verejné funkcie a to getRandomText a makeCaptcha. Funkcia getRandomText bude generovať náhodné znaky, z vopred zvoleného rozsahu, v ktorom sú vynechané znaky, ktoré sa na seba podobajú (0-O, 1-I, prípadne iné, ktoré sa na seba podobajú pri použitom fonte). Funkciu makeCaptcha necháme zatiaľ prázdnu.

Zdrojový kód:
class CaptchaGen {
    public function makeCaptcha($CaptchaText) {
        
    }
    
    public function getRandomText($Length) {
    //Vynechane znaky, ktore by mohli by mohli uzivatela zmylit (0-O, I-1)
    $Charset = "ABCDEFHJKLMNPRSTUVWXYZ23456789";
        
    $i = 0;
    $Value = "";
    while ($i < $Length) {
        $Value .= SubStr($Charset, rand(0, strlen($Charset) - 1), 1);
        $i++;
        }
    return ($Value);
    }
}

Do objektu CaptchaGen zadefinujeme konštanty a premenné, ktoré budeme používať vo viacerých jej metódach. Konštanty budú reprezentovať veľkosť obrázka, veľkosti písma, farebné rozsahy, cestu k súboru fontu. Premenné budú obsahovať dynamicky vytvorené farby písma.

Zdrojový kód:
class CaptchaGen {

     //Rozmery obrazka
    private const ImgWidth = 340;
    private const ImgHeight = 100;

    //Font
    private const CaptchaFont = "./captcha.ttf";
    
    //Klamlivy text na pozadi
    private const FakeTextSize = 20;
    private const FakeTextColorFrom = 160;
    private const FakeTextColorTo = 190;
    
    //Text Captcha obrazku
    private const TextSize = 28;
    private const TextColorFrom = 0;
    private const TextColorTo = 80;
    
    //Farby
    private $FakeTextColor = [];
    private $TextColors = [];
    private $White = null;
    
    public function makeCaptcha($CaptchaText)
     ...
}

V objekte vytvoríme funkciu initColors, ktorou inicializujeme premenné farieb v objekte. Na vytvorenie farieb je nutná referencia na obrázok, ktorá bude vstupným parametrom. Premennú White inicializujeme hodnotou bielej farby, ostatné premenné budú obsahovať polia z náhodne vybranými odtieňmi šedej v rámci povoleného rozsahu.

Zdrojový kód:
private function initColors($Img) {
        //Biela farba
        $this->White = imagecolorallocate($Img, 255, 255, 255);

        //Farby klamlivych textov
        for ($i = 0; $i < 20; $i++) {
            $val = rand(CaptchaGen::FakeTextColorFrom, CaptchaGen::FakeTextColorTo);
            $this->FakeTextColor[] = imagecolorallocate($Img, $val, $val, $val);
        }

        //Farby hlavneho textu
        for ($i = 0; $i < 10; $i++) {
            $val = rand(CaptchaGen::TextColorFrom, CaptchaGen::TextColorTo);
            $this->TextColors[] = imagecolorallocate($Img, $val, $val, $val);
        }
    }

Do funkcie makeCaptcha pridáme inicializáciu objektu obrázku, na ktorý budeme kresliť, zavoláme initColors na vytvorenie hodnôt farieb ,vyplníme pozadie obrázka bielou farbou a vrátime referenciu objektu obrázku na ktorý sme kreslili.

Zdrojový kód:
public function makeCaptcha($CaptchaText) {
    //Referencia obrazku v pamati
    $Img = imagecreatetruecolor(CaptchaGen::ImgWidth, CaptchaGen::ImgHeight);

    //Inicializacia pouzitych farieb
    $this->initColors($Img);

    //Pozadie obrazka vyplnime bielou farbou (pozadie)
    imagefilledrectangle($Img, 0, 0, CaptchaGen::ImgWidth - 1, CaptchaGen::ImgHeight - 1, $this->White);

    //Vratime referenciu na objekt obrazka
    return $Img;
}

Súbor captchagen.php nateraz uložíme a otvoríme captcha.php. Do súboru pridáme link na captchagen.php, inicializujeme sessions, vytvoríme inštanciu objektu CaptchaGen s ktorej si necháme vygenerovať text CAPTCHA kódu a následne aj obrázok. Vygenerovaný text vložíme do session a obrázok vrátime prehliadaču.

Zdrojový kód:
<?php

include_once ("captchagen.php");

session_start();

$cptch = new CaptchaGen();

//Vygenerujeme novu hodnotu Captcha kodu
$CaptchaText = $cptch->getRandomText(5);

$_SESSION["captcha"] = $CaptchaText;

//Vygenerujeme obrazkove spracovanie captcha kodu
$Img = $cptch->MakeCaptcha($CaptchaText);

//Vratime obrazok prehliadacu
@ob_end_clean();
header('Cache-Control: no-cache');
header('Content-type: image/png');
imagepng($Img);

?>

Pred samotným vrátením obrázku zavoláme funkciu ob_end_clean, ktorá sa postará o vyčistenie výstupného zásobníka (buffer) a uzavrie ho. V prípade, ak by sme to nespravili a niektorá funkčnosť pred nami by to zásobníka niečo zapísala (chyby, výstrahy pod.), bol by vrátený obsah obrázka pre prehliadač poškodený, nakoľko by obsah obsahoval neplatný obsah pri obrázku. Znak @ pred volaním funkcie slúži na potlačenie prípadných hlásení ktoré by metóda mohla vyvolať. Do hlavičky odpovede pre prehliadač pridáme príznak, aby obsah nebol cache-ovaný.

Súbor (captcha.php) uložíme a otvoríme v prehliadači. Ak sme postupovali správne, prehliadač zobrazí obrázok v tvare obdĺžnika vyplnený bielou farbou. V prípade ak prehliadač hlási, že obrázok je poškodený, odporúčam „zapoznámkovať“ posledné 4 riadky (od ob_end_clean po imagepng). Takto upravený dokument namiesto obrázku by mal vypísať chybové hlásenia, ktoré vás pravdepodobne nasmerujú pri hľadaní problému.

V prípade ak je všetko ok, pokračujeme so súborom CaptchaGen.php. Do funkcie makeCaptcha pred vrátenie referencie obrázka pridáme vykreslenie klamlivého textu do pozadia.

Zdrojový kód:
public function makeCaptcha($CaptchaText) {
     …
        //Nakreslime klamlivy text na pozadi
        for ($row = 0; $row < 4; $row++) {
            $faketext = $this->getRandomText(17);
            for ($i = 0; $i < strlen($faketext); $i++) { {
                    imagettftext($Img, CaptchaGen::FakeTextSize, rand(-30, 30), $i * 20, (23) * ($row + 1), $this->FakeTextColor[rand(0, sizeof($this->FakeTextColor) - 1)], CaptchaGen::CaptchaFont, $faketext[$i]);
                }
            }
        }
return $Img;
}

Obrázok s klamlivým pozadím

Nakreslíme 4 riadky po 17 náhodných znakov. Kreslíme jednotlivé znaky po jednom s náhodnou pozíciou z hora aj s náhodným miernym rotovaním (-30 až 30 stupňov). Súbor (captchagen.php) uložíme a opäť otvoríme captcha.php v prehliadači a uvidíme čo sa zmenilo.

Pokračujeme pridaním funkcie drawCaptchaText, ktorá bude kresliť text s fontom reálneho textu.

Zdrojový kód:
private function drawCaptchaText($Img, $StartFrom, $MinTop, $CaptchaText) {
    for ($i = 0; $i < strlen($CaptchaText); $i++) {
            imagettftext($Img, CaptchaGen::TextSize, rand(0, 6), $StartFrom + 30 * ($i), $MinTop + rand(30, 40), $this->TextColors[rand(0, sizeof($this->TextColors) - 1)], CaptchaGen::CaptchaFont, $CaptchaText[$i]);
    }
}

Volanie funkcie drawCaptchaText vložíme makeCaptcha.

Zdrojový kód:
public function makeCaptcha($CaptchaText) {
    ...
    //Nakreslime skutocny text
    $this->drawCaptchaText($Img, 100, 50, $CaptchaText);
        
    return $Img;
}

Skutočný kód
Pridané klamlivé kódy

Projekt uložíme a urobíme refresh stránky v prehliadači, kde by sme mali vidieť náhodne vygenerovaný text (zakaždým na rovnakej pozícii)

Tento text je skutočným kódom, ktorý budeme požadovať od návštevníka. Takto osamotený text by bol relatívne ľahko identifikovateľný, preto okolo neho pridáme ďalšie podobné texty, aby sme SPAMBota zmiatli. Do makeCaptcha za posledne pridaný riadok pridáme ďalšie volania drawCaptchaText, ktoré nám vytvoria okolité klamlivé texty.

Zdrojový kód:
...
    //Nakreslime skutocny text
    $this->drawCaptchaText($Img, 100, 50, $CaptchaText);
        
    //Kreslime klamlive texty
    $this->drawCaptchaText($Img, 0, 0, $this->getRandomText(11));
    $this->drawCaptchaText($Img, 0, 50, $this->getRandomText(3));
    $this->drawCaptchaText($Img, 270, 50, $this->getRandomText(2));
        
    return $Img;
...

Týmto sme ukončili samotné generovanie CAPTCHA kódu a teraz nám ostáva už len vytvoriť testovaciu stránku.

Otvoríme súbor index.php a vložíme do neho nasledujúci kód:

Zdrojový kód:
<!DOCTYPE html>
<html lang="sk">
<head>
    <meta charset="utf-8">
    <title>Captcha form</title>
    <style type="text/css" >
    #seccode
    {
        display:inline-block;
        width:170px; height:45px;
        background-image: url('captcha.php?<?php echo time(); ?>');
        background-repeat:no-repeat;
        background-position: -90px -50px;
    }    
    </style>
</head>
<body>
    <form action="" method="post">
    Ochranný kód: <div id="seccode"></div><br />
    <input type="text" name="codeval" /> <input type="submit" />
    </form>
</body>
</html>

Formulár s kódom

Pridali sme jednoduchú HTML stránku s formulárom, v ktorom je vložený DIV, ktorý nám bude slúžiť ako „zobrazovač“ nášho CAPTCHA kódu. V CSS kóde je dôležitá časť „background-position“, ktorou nastavíme zobrazenie pozadia tak, aby bola viditeľná len časť s platným kódom a „background-image“ ktorá načítava samotný obrázok. K samotnému názvu súboru captcha.php pripájame aj aktuálny timestamp, čím zaručíme aby bol obrázok vždy načítaný nanovo. Paradoxne „Cache-Control: no-cache'“ nie vždy funguje. Súbor uložíme a vyskúšame v prehliadači.

V prehliadači vidíme náhodne vygenerovaný kód a políčko na jeho zadanie.

Ostáva nám už len pridať kód na vyhodnotenie správnosti zadaného kódu. Preto do body pridáme PHP kód na jeho kontrolu.

Zdrojový kód:

<body>
<?php

session_start();

$codeval = filter_input(INPUT_POST, "codeval");
if ($codeval != "")
{
    if (strtoupper($codeval) == $_SESSION["captcha"])
    {
        print("<div style=\"color:green\">Captcha OK</div>");
    }
    else
    {
        print("<div style=\"color:red\">Captcha je nespravna</div>");
    }
}
?>
        <form action="" method="post">
....
</html>

Testovacia stránka je dokončená, môžeme si celý projekt uložiť a vyskúšať.
Kompletný zdrojový kód k projektu si môžete stiahnúťz nasledujúceho odkazu > link <

Codeblog
Diskusia

Žiadne príspevky v diskusii.

Nový príspevok

Na prispievanie do diskusie musíte byť prihlásený.