Web development: Ochrana proti robotům - CAPTCHA
| Zobrazit obsah | Zobrazit komentáře | Zobrazit pouze komentáře
|
Další zajímavá věc, která se může při tvorbě webu hodit, je ochrana formulářů proti robotům metodou CAPTCHA. Pro implementaci bude použito (X)HTML a PHP. Odkaz na ukázkový kód. Stahovat zdrojové kódy lze z Kavalek Solutions.
Popdůrné funkce
Pro zpřehlednění kódu vytvoříme několik klíčových funkcí, které se budou starat
jak o vygenerování kódu, tak pro jeho ověření a zárověň i pro odeslání e-mailu
(určeno spíše pro demonstraci celého projektu - není nijak vázáno na generování obrázku).
Pomocí funkce randomString() vygenerujeme náhodný řetězec zadané délky,
v našem případě budou postačovat 4 znaky - není problém rozšířit (v závislosti na velikosti
pozadí - background.png). Funkce returnCaptcha() generuje již
samotný obrázek (pomocí knihovny GD). Vygenerovaný kód je načten ze session, je použito pozadí
background.png a font font.ttf - fontů lze použít několik a následně upravit
tuto funkci, čímž bude obrázek ještě bezpečnější. Poslední funkcí je compareCaptcha(),
která porovnává MD5 hash kódu odeslaný z formuláře s MD5 hashem kódu uloženém v session.
Poslední podpůrnou funkcí je sendMail(), sloužící k odeslání obsahu formuláře mailem.
Funkce by mohla být mnohem jednodušší, ale mám ji takto hotovou pro své projekty, tak jsem ji
použil i v demonstraci funkce. Skript captcha.php vytváří již samotný obrázek
kódu - ten lze předat HTML tagu img jako parametr src díky
header("Content-Type: image/png");.
lib-captcha.php
<?php
// Random string
function randomString($length) {
$string = "";
for($i = 0; $i < $length; $i++) {
$char = rand(1, 35);
$char = ($char < 10) ? $char + 48 : $char + 55;
if($char == 79) $char++;
$string .= chr($char);
}
return $string;
}
// Return captcha
function returnCaptcha() {
$text = $_SESSION["captcha"];
$handle = imagecreatefrompng("./background.png");
$font = "./font.ttf";
for($i = 0; $i < strlen($text); $i++) {
$color = imagecolorallocate($handle, rand(0,200), rand(0,200), rand(0,255));
imagettftext($handle, rand(25, 40), rand(0, 20), 20 + ($i * 30), 40, $color, $font, $text[$i]);
}
imagecopy($handle, $handle, 0, 0, 0, 0, 150, 50);
imagepng($handle);
imagedestroy($handle);
}
// Compare captcha
function compareCaptcha($captcha, $hash) {
if(md5($captcha) == $hash) return true;
else return false;
}
?>
lib-sendmail.php
<?php
// Send mail
function sendMail($from, $to, $subject, $body) {
// Create e-mail MIME headers
$headers = "MIME-Version: 1.0\n";
$headers .= "Content-type: text/html; charset=iso-8859-2\n";
$headers .= "X-Priority: 3\n";
$headers .= "X-MSMail-Priority: Normal\n";
$headers .= "X-Mailer: php\n";
$headers .= "Return-Path: <" . $from . ">\n";
$headers .= "From: \"" . $from . "\" <" . $from . ">\n";
$headers .= "To: \"" . $to . "\" <" . $to . ">\n";
$headers .= "Subject: $subject\n";
$emailSource = $headers . "\n" . $body;
if(mail("\"" . $to . "\" <" . $to . ">", $subject, $body, $headers))
return true;
else
return false;
}
?>
captcha.php
<?php
header("Content-Type: image/png");
session_start();
require("./lib-captcha.php");
returnCaptcha();
?>
form-sendmail.php
<?php
session_start();
require("./lib-captcha.php");
require("./lib-sendmail.php");
$_SESSION["captcha"] = randomString(4);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-2" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Sendmail with CAPTCHA</title>
</head>
<body>
<?php
if(isset($_POST["action"]) && ($_POST["action"] == "sendmail")) {
if(compareCaptcha($_POST["captcha"], $_POST["hash"])) {
echo("Odesilam mail");
sendMail($_POST["mail"], "info@my-domain.com", "", $_POST["body"]);
}
} else {
?>
<div class="mailform">
<form action="form-sendmail.php" method="post" enctype="application/x-www-form-urlencoded">
<fieldset>
<legend>Napište nám</legend>
<p><label for="name">Vaše jméno:</label> <input id="name" name="name" type="text" tabindex="1" /></p>
<p><label for="mail">E-mail:</label> <input id="mail" name="mail" type="text" tabindex="2" /></p>
<p><label for="phone">Telefonní číslo:</label> <input id="phone" name="phone" type="text" tabindex="3" /></p>
<p><img src="captcha.php?<?= htmlspecialchars(SID); ?>" width="150" height="50" title="CAPTCHA" alt="CAPTCHA" /></p>
<p><label for="captcha">Ověření:</label> <input id="captcha" name="captcha" type="text" tabindex="4" /></p>
<p><textarea id="body" name="body" rows="1" cols="1" tabindex="5"></textarea></p>
<input id="hash" name="hash" type="hidden" value="<?php echo(md5($_SESSION["captcha"])); ?>" />
<input id="action" name="action" type="hidden" value="sendmail" />
<input type="submit" class="btn" value="Odeslat" tabindex="6" />
</fieldset>
</form>
</div>
<?php } ?>
</body>
</html>
Jak to funguje a co s tím?
Při načtení stránky s formulářem se aktivuje serverové sezení (tzv. session) a vygeneruje se náhodný
čtyřmístný (kombinace písmen a čísel) kód. Tento kód se uloží do session, odkud je použit jak pro
samotné vygenerování obrázku (captcha.php), tak i pro vytvoření MD5 hashe pro kontrolu.
Hash je po odeslání formuláře ověřen, souhlasí-li s hashem vloženého kódu, je zpracování odeslaných
dat korektní, v opačném případě jsou přijatá data zamítnuta.
Jako pozadí lze použít následující obrázek ve formátu PNG, různé fonty lze nelézt Googlem.
Co dál?
Tento článek sloužil jen jako demonstrace toho, jak jednoduchá je implementace ověřování formuláře metodou CAPTCHA.
To však nestačí, nebo spíše tím by vývoj neměl skončit. Bylo by vhodné soustředit použité funkce např. do jedné třídy,
aby vše bylo vhodně připraveno pohromadě pro použití ve větším projektu. Přepracovat řešení tak, aby se dalo snadno
používat jako šablona, a to buď pomocí nějakého šablonovacího systému (např. Smarty), nebo tak, aby bylo možné
blok kódu vkládat univerzálně. Prvním krokem by mohla být úprava action u fomuláře na
<?php echo($_SERVER["REQUEST_URI"]); ?>, případně řešení pomocí AJAXu, atd. Každý si
může upravit jak mu bude samo vyhovovat. Dále pak ověřování vyplnění povinných položek formuláře, platnost vstupních údajů pomocí regulárních výrazů atd.
Chyba
Díky použití tohoto skriptu na webu cestovní agentury PROWICOMT objevil Vilém Černohorský chybu v MSIE,
pokud použil skript na externím webu, vložený pomocí IFRAME. MSIE nechtěl spolupracovat a vše
řešil jako nepovolené cookies, nezobrazoval se kód, pouze podklad. Chybně jsem předpokládal kompilaci PHP
na hostingu s tagem --enable-trans-sid. Tuto nečekanou chybu jsem vyřešil vkládáním obrázku následujícím
způsobem captcha.php?<?= htmlspecialchars(SID); ?>, namísto captcha.php.
Blog
