Minule jsem vám prozradil, jak ochránit kontaktní formulář přes posíláním jednoho e-mailu na několik adres a dnes si povíme, jak od tohoto formuláře odlákat roboty úplně. V druhé části článku dokonce naleznete hotové řešení dokonalého kontaktního formuláře!
Byl jednou jeden robot
Robota si můžete představit jako aplikaci nebo nějaký skript, který projíždí stránky
a slídí, kde by našel nějaký formulář a mohl ho zběsile vyplnit nějakou reklamou
a odeslat. Přičemž si prohlíží HTML stránky čistě textově - nezobrazuje si grafiku
nebo kaskádové styly, jenom kouká na HTML tagy a jakmile uvidí <input type="text" name="email" />
,
tak mu začnou kapat sliny. Ihned vyplní všechny pole formuláře a zmáčkne všechny
tlačítka, které odesílají formulář, aby měl jistotu, že nedal třeba jenom náhled.
Takto jednoduše si lze představit robota - bohužel, tyto potvůrky se neustále vylepšují, takže některé si dokonce poradí s obrázkem, ze kterého musíte opsat znaky - tzv. CAPTCHA. I když vytvoříte speciální pole jenom pro roboty a skryjete ho pomocí CSS v domnění, že bude robot tak hloupý a vyplní ho taky, i zde můžete narazit. Avšak nepropadejme panice, řešení existuje !
Jak správně naklást pasti
Kontrolní otázka
Dobře nachytat robota lze na kontrolní otázce, na kterou nemá šanci znát odpověď. Může to být matematický příklad (3 + 4), doplnění do věty (skákal ___ přes oves) a podobně. Tato metoda je účinná, avšak obtěžuje uživatele - musí kvůli nám přemýšlet.
Captcha
I když jsem mluvil o tom, že někteří roboti opravdu umí rozpoznat tyto obrázky, není jich tolik, a proto se dá využít i toto řešení. Já bych ho však nedoporučoval - rozhodně není přístupné a znechucuje uživatele ještě více než kontrolní otázka. Já je přímo nesnáším - někdy jsou tak šílené, že se na ten obrázek opravdu musíte dívat nejmíň půl minuty, abyste odtušili nakreslené znaky.
Náhled
Na tomto blogu mám u přidávání komentářů pouze tlačítko “Náhled”. Odeslat příspěvek lze tedy až poté, co si ho prohlédnete. Věřte nebo ne, pouze toto stačí k odstrašení robotů (a doufám, že mi to ještě nějakou dobu vydrží). Bohužel je takovéto řešení použitelné jen v některých případech - nejčastěji v diskusích.
JavaScript
I když ty potvůrky umí všelicos, JavaScript ještě ne 🙂.
Tedy proč toho nevyužít - dělá se to tak, že pokud uživatel nemá JavaScript nebo
ho má vypnutý, tak se standardně nabízí nějaká kontrolní otázka a pokud je JavaScript
k dispozici, tak výsledek této otázky vloží do formuláře pomocí
<input type="hidden" ... />
a otázka samotná se již nezobrazí a uživatele neobtěžuje.
Řešení kontaktního formuláře
Připravil jsem kontaktní formulář, který využívá poslední pasti na roboty a to JavaScriptu.
Jelikož dneska letí XHTML, tak jsem si nemohl dovolit pouhé document.write
, jelikož
to specifikace nedovoluje. Proto je vkládání JavaScriptem řešeno elegantněji pomocí
DOM.
Kdyby se vám ve formuláři pozastavoval zrak nad tagem <label>
, tak vězte, že to
je svázání popisku s příslušným polem. Po kliknutí na popisek se kurzor přemístí
do pole, které k tomuto popisku patří. Každý přístupný formulář musí tento tag
obsahovat.
Celé řešení se skládá ze dvou souborů - samotný formulář a třída, která pracuje s daty, které jsou přes tento formulář odeslány.
napiste-nam.php
<?php
require_once "./MailForm.php";
$showForm = true;
if ( $_POST ) {
$form = new MailForm( $_POST[ "email" ], $_POST[ "text" ], $_POST[ "spamCheck" ],
$_POST[ "numbers" ] );
echo "<p>";
if ( $form->isValid( ) ) {
$showForm = false;
$form->sendMail( );
}
else {
$form->printErrors( );
}
echo "</p>\\n";
}
if ( $showForm ) {
$first = rand( 0, 9 );
$second = rand( 0, 9 );
?>
<h2>Napište nám</h2>
<form action="napiste-nam.php" method="post">
<div id="myform">
<label>
Váš e-mail:
<input type="text" name="email" size="40"<?php
if ( !empty( $_POST[ "email" ] ) ) {
echo " value=\\"".$_POST[ "email" ]."\\"";
} ?> />
</label><br />
(není povinný, vyplňte pokud chcete dostat odpověď)
<br /><br />
<label>
Text e-mailu:<br />
<textarea name="text" cols="42" rows="10"><?php
if ( !empty( $_POST[ "text" ] ) ) {
echo $_POST[ "text" ];
}
?></textarea>
</label>
<br /><br />
<noscript>
<label title="Vyplňte výsledek součtu těchto dvou čísel (ochrana před roboty)">
Kolik je <?php echo $first." + ".$second ?>:
<input type="text" name="spamCheck" size="10" />
</label>
<br /><br />
</noscript>
<script type="text/javascript">
/* <![CDATA[ */
spamcheck = document.createElement( ''input'' );
spamcheck.setAttribute( ''type'', ''hidden'' );
spamcheck.setAttribute( ''name'', ''spamCheck'' );
spamcheck.setAttribute( ''value'', <?php echo $first ?> + <?php echo $second ?> );
document.getElementById( ''myform'' ).appendChild( spamcheck );
/* ]]> */
</script>
<input type="hidden" name="numbers" value="<?php echo $first.$second ?>" />
<input type="submit" name="btn" value="Odeslat" />
</div>
</form>
<?php
}
?>
MailForm.php
<?php
class MailForm {
private $mail;
private $text;
private $spamCheck;
private $numbers;
private $errors;
public function __construct( $mail, $text, $spamCheck, $numbers ) {
$this->mail = $mail;
$this->text = $text;
$this->spamCheck = $spamCheck;
$this->numbers = $numbers;
$errors = array();
}
public function isValid( ) {
if ( empty( $this->mail ) ) {
$this->mail = "[email protected]";
}
elseif ( !ereg( "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$",
$this->mail ) ) {
$this->errors[] = "E-mail byl vyplněn ve špatném formátu !";
}
if ( empty( $this->text ) ) {
$this->errors[] = "Nebyl vyplněn text e-mailu !";
}
if ( empty( $this->spamCheck ) ) {
$this->errors[] = "Nebyla vyplněna kontrolní otázka !";
}
elseif ( $this->spamCheck != ( $this->numbers[ 0 ] + $this->numbers[ 1 ] ) ) {
$this->errors[] = "Nebyla správně zodpovězena kontrolní otázka !";
}
if ( empty( $this->errors ) ) {
return true;
}
else {
return false;
}
}
public function sendMail( ) {
$obsah = iconv( "utf-8", "iso-8859-2", $this->text );
if ( mail( "[email protected]", "E-mail z webu", $obsah, "From: ".
$this->mail."\\nContent-Type: text/plain; charset=iso-8859-2\\n" ) ) {
echo "E-mail byl odeslán.";
}
else {
echo "E-mail se nepodařilo odeslat !";
}
}
public function printErrors( ) {
for ( $i = 0; $i < count( $this->errors ); $i++ ) {
echo $this->errors[ $i ]."<br />\\n";
}
}
}
?>
Kódování e-mailu převádím z utf-8 na iso-8859-2, protože jsem zjistil, že některé webmaily si s unicode neporadí.