Systemvoraussetzung
- Linux
- Windows
- PHP 4 >= 4.0.0RC2
- PHP 5
- GDBibliothek
(> gd1.6)
- FreeTypeBibliothek
Datei(en)
captcha.php, captcha_check.php, captcha_demo.php
Problem
Sie haben ein Webformular und wollen sicher sein, dass dieses Formular
wirklich von einem Menschen ausgefüllt wurde und nicht etwa durch ein
Skript auf einem anderen Server. Dieses Szenario kommt häufiger vor, als Sie
denken. Gerade im Bereich Gewinnspiele wird diese Variante immer wieder
gerne ausgenutzt.
Ein Mitglied bezahlt z. B. 15 Euro im Monat und wird dafür automatisch,
also mithilfe eines Skripts, in mehreren 1000 Gewinnspielen eingetragen.
Dieses Mitglied war nie auf einer der betreffenden Seiten und ist nicht an den
Produkten auf dieser Seite interessiert – es will nur einen Gewinn „abstauben“.
Verlierer sind dann die ehrlichen Mitspieler, die das Formular wirklich selbst
ausgefüllt haben. Sie können den Eintragungsdiensten aber mit ein wenig
Aufwand entgegenwirken.
Im Kapitel CURL werden Sie später ein Programm sehen, das genau diese
Aufgabe erledigt, also sich auf einer fremden Seite einträgt, ohne dass Sie
dafür etwas machen müssen. Wir zeigen Ihnen dieses Programm nur, damit
Sie Ihre Formulare besser schützen können.
Lösung
Das Zauberwort für das Problem der Eintragungsdienste heißt Captcha (Completely
Automated Public TuringTest
to Tell Computers and Humans Apart).
Dabei geht es darum, eine Aufgabe zu stellen, die es zu lösen gilt. Die Aufgabe sollte so schwer sein, dass Sie ein Computer (z. B. ein Bot) nur schwer, ein
Mensch dagegen leicht lösen kann.
Nachfolgend stellen wir einen solchen Captcha vor. Im Buch sehen Sie bei den
Beispielen nur die Graustufen – tatsächlich aber erzeugt der hier vorgestellte
Programmcode Bilder mit Farbverläufen und jeweils unterschiedlichen Farben
für jeden Buchstaben.
Der Mensch wird aufgefordert, die nachfolgenden Buchstaben und Zahlen in
ein Textfeld einzugeben. Nach dem Senden des Formulars wird dann geprüft,
ob das Captcha einwandfrei gelöst wurde.
Diese Aufgabe ist relativ leicht, denn der Mensch muss nur die Buchstaben in
der richtigen Reihenfolge in das Textfeld schreiben. In der Regel unterscheidet
man dabei nicht zwischen Groß- und
Kleinschreibung, da dieser Unterschied
nicht immer sofort ersichtlich ist. Sie sehen an dem „S“ im Bild, das man nicht
mit hundertprozentiger Sicherheit sagen kann, ob es sich um einen Groß- oder
Kleinbuchstaben handelt.
Weiterhin sollte man nicht alle Buchstaben und Zahlen für einen Captcha
heranziehen – auch dort besteht Verwechslungsgefahr. Folgende Buchstaben
und Zahlen sollten ausgeschlossen werden.
- L und l das kleine und große L
- I und i das kleine und große I
- O und o das kleine und große O
- 1 Die Zahl eins
- 0 Die Zahl null
Eine weitere Möglichkeit ist auch die nachfolgende Variante. Dabei muss der
Mensch die folgende leichte Rechenaufgabe lösen.
Die Aufgabe sollte für einen Menschen leicht sein. Die Lösung kann also in
diesem Fall nur 27 sein. Sie könnten die Aufgabe auch noch erschweren und
Ihren Captcha mit mehreren Möglichkeiten ausstatten.
- Addition
- Subtraktion
- Multiplikation
- Division
Wenn Sie diese Möglichkeiten mischen, erschweren Sie die Aufgabe. Bitte
denken Sie aber daran, dass die Aufgabe nicht zu schwer wird. Es ist nicht
sinnvoll, wenn Ihre Besucher frustriert das Formular nicht ausfüllen, weil sie
die Aufgaben nicht lösen können.
Eines sollten Sie berücksichtigen! Menschen mit einer Sehstörung werden
Sie bei dieser Variante ausschließen, sie benötigen einen Captcha, der z. B.
einen Text aufsagt. Dieser gesprochene Text muss dann in ein Formularfeld
eingegeben werden.
Wir wollen uns nun dem Programmcode widmen und schauen uns erst einmal
die Funktionen an, die für den Aufbau des CaptchaBildes
verantwortlich ist.
function MakeAlphabet()
@return array $alphabet
Die Funktion MakeAlphabet() soll uns ein Array erzeugen, in dem Klein,
Großbuchstaben und Zahlen enthalten sind. Allerdings wollen wir wegen der
Verwechslungsgefahr nicht alle Buchstaben und Zahlen haben. Daher greifen
wir auf die asciiTabelle
zu und lassen zuerst alle Großbuchstaben in das
Array schreiben (45). Die Großbuchstaben in der asciiTabelle
befinden sich
an der Position 6590.
Da wir das I, das L und das O ausschließen wollen,
berücksichtigen wir das mit einer Abfrage (44).
Im nächsten Schritt erstellen wir die Kleinbuchstaben. In der asciiTabelle
finden wir diese Buchstaben an der Stelle 97122.
Auch hier schließen wir das
i, das l und das o mit einer IFAbfrage
aus (50) und speichern die restlichen
Buchstaben in unser Array (51).
Zum Schluss müssen wir nur noch die Zahlen speichern. In der asciiTabelle
finden wir die Zahlen an der Position 4857.
Wegen der Verwechslungsgefahr
schließen wir hier die 1 und die 0 aus (56) und speichern die restlichen Zahlen
in unser Array (57).
Momentan befinden sich alle Buchstaben und Zahlen noch geordnet in dem
Array. Später werden wir das Array durchmischen, sodass wir willkürliche
Ausgaben erzeugen können.
Sie hätten die geraden erzeugten Buchstaben und Zahlen auch mit range()
erzeugen können, ich finde die beschriebene Variante allerdings besser, da Sie
hier gezielter Einfluss auf die Ausgabe haben. Dennoch möchte ich Ihnen die
AlternativVariante
mit range nicht vorenthalten.
- $grossbuchstaben = range('A','Z');
- $kleinbuchstaben = range('a','z');
- $zahlen = range(0,9);
Als Rückgabewert liefert die Funktion MakeAlphabet() ein Array mit allen
Buchstaben und Zahlen.
041:
042: 043: 044: 045: 046: 047: 048: 049: 050: 051: 052: 053: 054: 055: 056: 057: 058: 059: 060: 061:
|
function MakeAlphabet(){
// Grossbuchstaben erzeugen for ($x = 65; $x <= 90; $x++) { if($x != 73 && $x != 76 && $x != 79) $alphabet[] = chr($x); }
// Kleinbuchstaben erzeugen for ($x = 97; $x <= 122; $x++) { if($x != 105 && $x != 108 && $x != 111) $alphabet[] = chr($x); }
// Zahlen erzeugen for ($x = 48; $x <= 57; $x++) { if($x != 48 && $x != 49) $alphabet[] = chr($x); } return $alphabet; }
|
Beispiel 3.6: captcha.php
function makeBorder($imgWidth,$imgHeight,$im,$color)
@param integer $imgWidth
@param integer $imgHeight
@param resource $im
@param array $color
@return void
Die Funktion makeBorder() setzt lediglich einen Außenrahmen um unser
CaptchaBild.
Als Parameter erwartet die Funktion die Bildbreite ($imgWidth),
die Bildhöhe ($imgHeight), den Zeiger auf das Bild ($im) sowie die Farbe im
RGBModus
($color).
Wir definieren als Erstes die Farbe und fangen danach an zu zeichnen. Dabei
geben wir die Werte immer als X/YKoordinaten
an. Der waagrechte Strich
oben beginnt bei den Koordinaten 0/0 und endet bei Bildbreite/0 (74). Der
untere Strich beginnt bei den Koordinaten 0/Bildhöhe1
(wichtig ist hier, die 1
abzuziehen, da man sonst die Linie nicht sehen würde) und Bildbreite/Bildhöhe1
(75).
Der linke senkrechte Strich beginnt bei den Koordinaten 0/0 und endet bei
0/Bildhöhe (76). Der rechte senkrechte Strich beginnt bei den Koordinaten
Bildbreite1/
0 und endet bei Bildbreite1/
Bildhöhe (77).
Damit haben wir erfolgreich einen Rahmen um die Grafik gezogen. Damit
der Rahmen nachher wirklich vollkommen ist und nicht von Buchstaben oder
Zahlen überlagert wird, rufen wir diese Funktion erst zum Schluss auf.
073:
074: 075: 076: 077:
078
|
function makeBorder($imgWidth,$imgHeight,$im,$color){
imageline($im,0,0,$imgWidth,0,$color); imageline($im,0,$imgHeight-1,$imgWidth,$imgHeight-1,$color); imageline($im,0,0,0,$imgHeight,$color); imageline($im,$imgWidth-1,0,$imgWidth-1,$imgHeight,$color); }
|
Beispiel 3.7: captcha.php
function makeGradient($fromRGB,$toRGB,$imgHeight)
@param array $fromRGB
@param array $toRGB
@param integer $imgHeight
@return array $color
Die Funktion makeGradient() sorgt für den Farbverlauf im CaptchaBild.
Diese Funktion muss als Erstes aufgerufen werden, da die Buchstaben, Zahlen
und der Außenrahmen auf ihr platziert werden sollen. Sie müssen sich die
Grafikerstellung wie Ebenen vorstellen, also von hinten nach vorne denken.
Die Funktion erwartet als Parameter den RGBWert
für den Start des Farbverlaufs
($fromRGB), den RGBWert
für das Ende des Farbverlaufs ($toRGB) und
als letzten Parameter die Bildhöhe ($imgHeight). Der Farbverlauf beginnt im
oberen Teil des Bildes und verläuft dann nach unten.
Für den Farbverlauf müssen wir im Vorfeld einige Berechnungen durchführen.
Damit Sie diese Berechnungen an einem Beispiel nachvollziehen können, sehen
Sie nachfolgend, mit welchen Parametern die Funktion aufgerufen wurde.
132:
133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143:
144:
160:
161:
| $imgHeight = 80;//80
// Farbverlauf RGB-Wert Start und RGB-Wert Ende
$fromRGB = array(
253,
214,
0
);
$toRGB = array(
215,
81,
6
);
// Erstellt den Farbverlauf
$colorRGB = makeGradient($fromRGB,$toRGB,$imgHeight);
|
Beispiel 3.8: captcha.php
Beispiel 3.9: captcha.php
Zuerst berechnen wir die Differenz der einzelnen RGBWerte
von $fromRGB
und $toRGB. Diese Differenz benötigen wir später für die Schrittbereiche der
einzelnen Farblinien.
$diffR = 253 – 215 = 38
$diffG = 214 – 81 = 133
$diffB = 0 – 6 = 6
Diese Differenzbeträge dividieren wir im nächsten Schritt durch die Höhe des
Bildes und erhalten dann die tatsächlichen Schrittbereiche.
$maxR = 38 / 80 = 0.475
$maxG = 133 / 80 = 1.6625
$maxB = 6
/ 80 = 0.075
Somit ergeben sich z. B. für die Farbe Rot folgende Schrittwerte. Diese Schrittwerte
werden nachher in der FORSchleife
(103110)
berechnet. Wir zeigen
Ihnen hier nicht alle 80 Schritte, sondern nur einen Auszug:
253 , 252.525 , 252.05 , 251.575 . . . 215
Diese Schritte bestimmen wir für alle drei Farbwerte, also Rot, Grün und
Blau. Wir speichern (99-101)
diese Werte in einer Variablen zwischen, um
die einzelnen Schrittwerte nachher innerhalb der FOR-Schleife
addieren zu
können.
Im letzten Schritt erstellen wir ein Array mit den einzelnen RGBWerten
für den
Verlauf. Da unsere Höhe hier 80 Pixel ist, müssen wir auch unsere Schleife (103)
80mal
durchlaufen. Für jede Zeile (das Bild besteht aus 80 Zeilen) müssen
wir jetzt den Wert, also R (104), G (105) und B (106) berechnen. Die einzelnen
RGBWerte
dürfen nur positiv sein, daher formatieren wir die Werte mit abs()
in positive Werte um.
Wichtig ist, dass wir bei jedem Durchlauf den Schrittwert erhöhen. Für die
Farbe Rot haben wir einen Schrittwert von 0.475. Somit ergibt sich beim
zweiten Durchlauf ein Wert von 0.95, beim dritten Durchlauf ein Wert von
1.425 usw.
Diesen Wert ziehen wir dann jeweils von unserem Grundwert (im Beispiel Rot:
253) ab. Diese Berechnungen müssen wir für alle drei Farben machen und
erhalten dann ein Array mit allen RGBWerten
für die jeweiligen Zeilen.
089:
090: 091: 092: 093: 094: 095: 096: 097: 098: 099: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114:
|
function makeGradient($fromRGB,$toRGB,$imgHeight){
$diffR = ($fromRGB[0]-$toRGB[0]); //38 $diffG = ($fromRGB[1]-$toRGB[1]); //133 $diffB = ($fromRGB[2]-$toRGB[2]); // -6
$maxR = $diffR / $imgHeight; // 0.475 $maxG = $diffG / $imgHeight; // 1,66 $maxB = $diffB / $imgHeight; // 0.075
$maxNR = $maxR; $maxNG = $maxG; $maxNB = $maxB;
for($x=0;$x<$imgHeight;$x++){
$color['r'][$x] = abs($fromRGB[0] - $maxR); $color['g'][$x] = abs($fromRGB[1] - $maxG); $color['b'][$x] = abs($fromRGB[2] - $maxB); $maxR += $maxNR; $maxG += $maxNG; $maxB += $maxNB; } return $color; }
|
Beispiel 3.10: captcha.php
Wir haben nun alle notwendigen Funktionen erklärt. Jetzt müssen wir den
Programmcode für die einzelnen Funktionen erläutern. Wie bei fast jedem
Programm kommt auch dieses nicht ohne ein paar Konfigurationselemente
aus.
Die Konfigurationsparameter bestehen aus dem Pfad (119) zu der TrueTypeSchrift
(geben Sie hier den absoluten Pfad an, da die Schrift sonst eventuell
nicht eingebunden werden kann), der Schriftgröße (128), der Bildbreite (131)
und der Bildhöhe (132).
Weiterhin müssen Sie in einem Array (135) den RGBWert
für den Farbverlauf
angeben. Dabei handelt es sich um den Startwert. Den RGBWert
für den
Endwert müssen Sie ebenfalls in einem Array (140) definieren. Als letzten Parameter
müssen Sie noch eine Borderfarbe (147) für Ihr CaptchaBild
angeben,
diese selbstverständlich auch als RGBWert.
Sehr wichtig ist die Variable $captchaDir (125)! Sie sollten hier ein Verzeichnis
angeben, in dem die CaptchaDateien
gespeichert werden. Vergewissern Sie
sich, dass dieses Verzeichnis Schreibrechte besitzt und Sie in dieses Verzeichnis
auch schreiben dürfen. Anhand der darin liegenden CaptchaDateien
wird
auch nachher geprüft, ob das Captcha gelöst wurde. Bei jedem Aufruf dieses
Programms wird eine CaptchaDatei
nach folgendem Muster gebildet.
116:
117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133:
134: 135: 136: 137: 138: 139: 140:
141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151:
| // TTF-Schrift
// Sie sollten hier unbedingt den absoluten Pfad angeben, da ansonsten // eventuell die TTF-Datei nicht eingebunden werden kann! $fileTTF = '/homepages/29/d151852282/htdocs/kochbuch/bddavinc.ttf';
// Verzeichnis für die Captcha-Bilder (muss Schreibrechte besitzen!) // Ausserdem sollten in diesem Ordner nur die Bilder gespeichert werden // da das Programm in regelmaessigen Abstaenden dieses leert! // Kein abschliessenden Slash benutzen! $captchaDir = 'captchadir';
// Schriftgröße $size = 30;
//Bildgroesse $imgWidth = 220;//200 $imgHeight = 80;//80
// Farbverlauf RGB-Wert Start und RGB-Wert Ende
$fromRGB = array(
253,
214,
0
);
$toRGB = array(
215,
81,
6
);
// Randfarbe $colorBorder = array(
154,
53,
2
);
|
Beispiel 3.11: captcha.php
Sämtliche Parameter haben wir gesetzt. Nun können wir den Programmcode
näher unter die Lupe nehmen. Zuerst definieren wir einen Header (1), der für
die Auslieferung unseres PNGBild
zuständig ist. Die Datei für den Aufbau
des CaptchaBilds
wird nicht direkt inkludiert, sondern über ein ImageTag
angesprochen.
01:
|
<img src="captcha.php?codeCaptcha=<?php echo $codeCaptcha; ?>">
|
Wir müssen im nächsten Schritt eine Arbeitsfläche (ein neues Bild) erzeugen
(232), auf der wir nachher die einzelnen Ebenen platzieren. Sollte die Initialisierung
fehlschlagen, wird eine Fehlermeldung zurückgegeben. Im Erfolgsfall
erhalten wir einen Zeiger auf das neue Bild.
Diesen Zeiger müssen wir jetzt bei allen Bildmanipulationen mit angeben, da
ansonsten die Aktion nicht ausgeführt wird. Mit den Werten aus unserem Array
für die Borderfarbe erzeugen wir (235) eine Farb-ID,
die wir später an unsere
Funktion makeBorder($imgWidth,$imgHeight,$im,$colorB) übergeben
müssen. Anhand dieser Farb-ID
wird der Rahmen um das Bild gezogen.
Wie erwähnt, sollten Sie sich jede Aktion als Ebene vorstellen. Wir müssen also
zunächst den Farbverlauf erzeugen, da über diesen die Buchstaben und Zahlen
sowie der Rahmen gelegt werden sollen. Wir rufen daher unsere Funktion
makeGradient() (238) auf und erhalten als Rückgabewert ein Array mit allen
Farbwerten für jede Zeile (hier im Beispiel hat das Bild 80 Zeilen).
Wir durchlaufen (240) jetzt 80-mal
die FOR-Schleife
und zeichnen dabei immer
eine Linie. Die einzelnen Farbwerte liegen sortiert in unserem Array (241243),
sodass wir die jeweiligen RGB-Werte
in eine Variable überführen können.
Anhand dieser Werte erzeugen wir wieder eine FarbID
(244) für die zu zeichnende
Linie (245). Sie erinnern sich sicherlich noch an die X/Y-Koordinaten.
Die XKoordinate
wird durch die FOR-Schleife
immer um eins erhöht, somit
füllen sich nach und nach die Linien. Die Breite ist fest und muss daher nicht
weiter berücksichtigt werden.
Wenn Sie vorhaben, den Farbverlauf nicht von oben nach unten, sondern von
links nach rechts zu zeichnen, ist auch das auch kein Problem. In diesem
Fall müssen Sie lediglich die X/Y-Koordinaten
anders bestimmen und die
FOR-Schleife
durch die Breitenangabe laufen lassen.
230:
231: 232:
233: 234: 235:
236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246:
| header("Content-type: image/png");
$im = @imagecreate($imgWidth, $imgHeight) or die("GD! Initialisierung fehlgeschlagen");
// Definiert die Farbe für den Border $colorB = imagecolorallocate($im,$colorBorder[0],$colorBorder[1],
$colorBorder[2]);
// Erstellt den Farbverlauf $colorRGB = makeGradient($fromRGB,$toRGB,$imgHeight);
for($x=0;$x<$imgHeight;$x++){
$r = $colorRGB['r'][$x]; $g = $colorRGB['g'][$x]; $b = $colorRGB['b'][$x]; $color = imagecolorallocate($im,$r,$g,$b); imageline($im,0,$x,$imgWidth,$x,$color); }
|
Beispiel 3.12: captcha_math.php
Wir haben nun den Farbverlauf erstellt und müssen im nächsten Schritt die
einzelnen Buchstaben und Zahlen auf das Bild platzieren. Wir lassen uns
zuerst durch die Funktion MakeAlphabet() ein Array (172) aus Buchstaben
und Zahlen erzeugen.
Damit der Inhalt des Arrays nicht so ordentlich sortiert bleibt und wir willkürliche
Ausgaben erhalten, würfeln wir mit shuffle() (175) durch unser
Array.
Die Buchstaben sollen nicht alle die gleiche Farbe haben, sondern verschiedenfarbig
sein. Der Farbrahmen für RGBWerte
liegt zwischen 0255,
und wir
erstellen mit range() (177) aus diesem Bereich ein Array (177), das wir im
Anschluss daran noch durchwürfeln (178).
Da wir sechs Buchstaben haben, benötigen wir auch sechs verschiedene Farb-IDs
(180-187). Vermutlich fragen Sie sich jetzt, weshalb hier keine FORSchleife
herangezogen wurde. Der Grund: Beim Testen mit einer FORSchleife
kam es
vor, dass die letzten FarbIDs
nicht gesetzt wurden und daher die letzten 34
Buchstaben alle die gleiche Farbe hatten.
171:
172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185:
186:
187: | //Wortliste erstellen
$alphabet = MakeAlphabet();
// Array des Alphabets durchwürfeln shuffle($alphabet);
$mix = range(0,255); shuffle($mix);
$colors=array( imagecolorallocate($im,$mix[0],$mix[6],$mix[12]), imagecolorallocate($im,$mix[1],$mix[7],$mix[13]), imagecolorallocate($im,$mix[2],$mix[8],$mix[14]), imagecolorallocate($im,$mix[3],$mix[9],$mix[15]), imagecolorallocate($im,$mix[4],$mix[10],$mix[16]), imagecolorallocate($im,$mix[5],$mix[11],$mix[17]) );
|
Beispiel 3.13: captcha.php
Da nun alle Farben für die Buchstaben und Zahlen gesetzt sind, können wir
die einzelnen Zeichen auf das Bild platzieren. Wir setzen für jeden Durchlauf
(188) den Neigungswinkel (190) von einem Buchstaben oder einer Zahl, damit
diese entweder nach links oder rechts geneigt sind.
Weiterhin wollen wir erreichen, dass die Buchstaben und Zahlen nicht in einer
Reihe, sondern nach oben und unten versetzt dargestellt werden. Wir ermitteln
also auch hier für jedes Element eine Zufallszahl zwischen der Schriftgröße
und der Bildhöhe20.
Wir haben nun alle Informationen gesammelt und können den Buchstaben oder
die Zahl zeichnen (192). Mit dem Zeiger ($im), dem Neigungswinkel ($angel),
der XPosition
(also wie viele Pixel von links $
next) und der YPosition
(also
wie viele Pixel von oben $
y) haben wir eine wesentliche Grundlage für die
Positionierung.
Wir müssen noch die Farbe bestimmen ($colors), den Pfad zur TrueTypeDatei
($fileTTF) angeben und zum Schluss den jeweiligen Buchstaben oder
die Zahl ($alphabet) auswählen. Alle Informationen sorgen dafür, dass das
Bild mit den Informationen beschrieben wird. Beim nächsten Durchlauf müssen
wir allerdings darauf achten, dass das neu zu schreibende Zeichen in seiner
XPosition
nach rechts verschoben wird (193).
Dies erledigen wir mit einer kleinen mathematischen Aufgabe und erhalten
danach den neuen Punkt für das Zeichen. Zum Schluss einer jeden FORSchleife
müssen wir den jeweiligen Buchstaben oder die Zahl an eine Variable anhängen
(194) – aus dieser erstellen wir später die CaptchaDatei
für die Überprüfung.
Sie können Ihr Captcha an dieser Stelle noch ein wenig komplizierter machen.
Statt einer TrueTypeDatei
könnten Sie hier wahllos mehrere zufällig auswählen.
Somit erreichen Sie nochmals einen höheren Schutz. Speichern Sie dafür
einfach sechs verschiedene Pfadangaben in ein Array und lassen dieses vor
der FORSchleife
durchwürfeln. Innerhalb der FORSchleife
greifen Sie dann
mit $x auf das jeweilige Element zu.
187:
188: 189: 190: 191: 192:
193:
194: 195: 196:
| // Zeichnet die Buchstaben und baut den Dateinamen auf
for($x=0;$x<6;$x++){
$angel = rand(-25,25); $y = rand($size,$imgHeight-20); imagettftext($im, $size, $angel, $next, $y, $colors[$x],
$fileTTF,$alphabet[$x]);
$next += $size + ($imgWidth/$size); $fileName .= $alphabet[$x];
}
|
Beispiel 3.14: captcha.php
Wir sind jetzt fast am Ende und müssen nur noch ein paar Kleinigkeiten für das
CaptchaBild
erledigen. Wir rufen die Funktion makeBorder() (199) auf und
lassen uns einen Rahmen um das Bild ziehen. Im nächsten Schritt überprüfen
wir, ob die übermittelte HashSumme
korrekt ist, also aus Buchstaben von a
bis f und Zahlen von 0 bis 9 besteht.
Weiterhin muss die HashSumme
32 Zeichen lang sein. Sollte die Überprüfung
(202) fehlschlagen, erstellen wir eine neue HashSumme
(203). Das Captcha
kann mit dieser HashSumme
zwar nicht mehr gelöst werden, der Form halber
erstellen wir diese aber.
Unser CaptchaBild
ist jetzt fertig und muss nur noch in ein Verzeichnis gespeichert
werden (206), da wir dieses Bild später für die Überprüfung benötigen.
Den Dateinamen bilden wir aus der HashSumme
und den sechs Buchstaben
bzw. Zahlen. Als Dateiendung erhält das Bild die Endung .png. Wir können nun
den Speicher für das Bild freigeben (208) und mit readfile() (211) das Bild
letztendlich ausgeben.
198:
199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: | // Border erstellen
makeBorder($imgWidth,$imgHeight,$im,$colorB);
// Uebermittelter Hash-Wert ueberpruefen if(!preg_match('/^[a-f0-9]{32}$/',$_GET['codeCaptcha'])) $_GET['codeCaptcha'] = md5(microtime());
// Image speichern imagepng($im,$captchaDir.'/'.$_GET['codeCaptcha'].'_'.$fileName.'.png');
imagedestroy($im);
// Bild ausgeben readfile($captchaDir.'/'.$_GET['codeCaptcha'].'_'.$fileName.'.png'); |
Beispiel 3.15: captcha.php
Unser CaptchaBild
ist fertig! Jetzt müssen wir uns noch den Aufruf der
CaptchaDatei
in unserem Formular und die Überprüfung eines jeden Captchas
anschauen. Wir beginnen zuerst mit dem Formular.
Wie Sie sehen, können Sie mit ein paar wenigen Handgriffen das Captcha einbauen
und der Besucher wird mit Ihrem Formular arbeiten. Wenn das Formular
aufgerufen wird, erstellen wir zuerst eine HashSumme
aus der aktuellen Zeit
(27). Um wirklich eine fast eindeutige HashSumme
zu generieren, werden
nicht nur die Sekunden, sondern auch die Mikrosekunden für die HashSumme
herangezogen.
Die Wahrscheinlichkeit, dass zwei Menschen zur gleichen Zeit dieses Captcha
starten, ist sehr gering. Sie müssten nicht nur in der gleichen Sekunde, sondern
auch zur gleichen Microsekunde das Formular starten – ein 6er im Lotto ist da
schon wahrscheinlicher.
Anhand der HashSumme
und des CaptchaBilds
erstellen wir den Dateinamen
für das Bild. Wie Sie sehen, beginnt der Dateiname mit der HashSumme,
gefolgt von einem Unterstrich und den sechs Buchstaben oder Zahlen, die in
diesem Bild angezeigt werden.
4b73f3081a465c95bb4d59108dc43eca_dVeUXh.png
Wir müssen nun in unserem Formular das CaptchaBild
anzeigen. Dafür nutzen
wir einen ImageTag
(28) und geben als Ziel (normalerweise steht hier ein
Bildname) die CaptchaDatei
an. Zusätzlich übermitteln wir die HashSumme,
aus der dann der Dateiname gebildet wird.
Irgendwie muss noch die HashSumme
beim Senden des Formulars übermittelt
werden. Dafür benutzen wir ein hiddenField
(30) – jetzt haben wir alles, was
wir brauchen, und das Formular kann gesendet werden.
Nach dem Senden des Formulars wird zuerst überprüft, ob der Besucher das
Formularfeld für die CaptchaPrüfung
ausgefüllt hat (8). Falls ja, wird die Datei
für die CaptchaÜberprüfung
inkludiert (10) und das Captcha geprüft. Sie
erhalten als Rückgabewert der Überprüfung in der Variable $resultCaptcha
entweder „TRUE“ oder „FALSE“. TRUE heißt in diesem Fall, dass die Aufgabe
gelöst wurde, FALSE hingegen besagt, dass die Aufgabe falsch war.
86 3 Formulare
Egal, ob die Aufgabe richtig oder falsch war, es wird auf jedem Fall ein neues
CaptchaBild
mit einer neuen HashSumme
erstellt.
01:
02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
23: 24: 25: 26: 27: 28: 29: 30:
31:
32: 33: 34: 35: 36: |
<html>
<head>
<title>Demonstration - Captcha</title>
</head>
<body>
<!-- Captcha Check Begin -->
<?php
if(!empty($_POST['stringCaptcha']))
{
include_once('captcha_check.php');
if($resultCaptcha)
{
// Alles OK, Daten koennen gespeichert werden
echo 'Eingabe korrekt!';
}
else
{
// Captcha ist falsch - Fehler ausgeben
echo 'Eingabe falsch';
}
}
?>
<!-- Captcha Check End -->
<form name="CaptchaForm" method="post" action="">
<!-- Captcha Begin -->
<?php $codeCaptcha = md5(microtime()); ?>
<img src="captcha.php?codeCaptcha=<?php echo $codeCaptcha; ?>">
<br>
<input type="hidden" name="codeCaptcha" value="<?php echo
$codeCaptcha; ?>">
<input type="text" name="stringCaptcha">
<!-- Captcha End -->
<p><input type="submit" name="Submit" value="Wert prüfen"></p>
</form>
</body>
</html>
|
Beispiel 3.16: captcha_demo.php
Sie sehen hier, wie Sie das Captcha z. B. in Ihr Formular einbinden können.
Wir müssen uns jetzt noch die Datei anschauen, die unsere CaptchaDatei
überprüft.
In dieser Datei müssen Sie ein paar Konfigurationsparameter anpassen.
Sie müssen als Erstes das Verzeichnis angeben (82), in dem Ihre CaptchaBilder
gespeichert werden.
Vergessen Sie nicht, am Ende den Slash (/) mit anzugeben. Als weiteren Parameter
benötigen wir noch eine Zeit in Minuten (85), nachdem alte, nicht
gebrauchte Bilder (weil das Formular z. B. nicht abgesendet wurde) wieder
gelöscht werden können.
Das Formular inkludiert diese Datei und übermittelt uns dabei zum einen
den HashWert
und zum anderen das Feld mit den sechs Buchstaben oder
Zahlen. Die Funktion CheckCaptcha wird mit diesen Parametern und den
beiden Konfigurationsvariablen aufgerufen (88) und erhält als Rückgabewert
entweder „TRUE“ oder „FALSE“.
function CheckCaptcha($codeCaptcha,$stringCaptcha,$dir,$delFile=5)
@param string $codeCaptcha
@param string $stringCaptcha
@param string $dir
@param integer $delFile
@return bool
Nachdem die Funktion aufgerufen wurde, setzen wir zuerst unseren Rückgabewert
auf „FALSE“ (39). Im nächsten Schritt überprüfen wir unsere HashSumme
(42) auf Gültigkeit und im Anschluss daran noch die übermittelte Variable mit
den eingegebenen Buchstaben oder Zahlen (42).
Sie fragen sich vermutlich, weshalb wir bei der Überprüfung der Buchstabenlänge
(42) nicht auf sechs Stellen prüfen, sondern einen Wert zwischen 16
heranziehen. Im nächsten Beispiel wollen wir das Captcha ja auch Rechenaufgaben
erstellen lassen.
Die Lösungen von Rechenaufgaben haben in der Regel aber nur eine oder
zwei Stellen. Da wir keine andere Datei für die Überprüfung inkludieren
wollen, haben wir uns für diesen Weg entschieden. Das macht die Überprüfungsdatei
flexibler, da sie jetzt mit Mathematikaufgaben sowie Buchstaben/
Zahlenkombinationen umgehen kann.
Sollte einer der Parameter nicht unseren Kriterien entsprechen, beenden wir
die Funktion und geben „FALSE“ zurück – somit wurde das Captcha nicht
gelöst. War die Überprüfung erfolgreich, lesen wir das Verzeichnis mit den
CaptchaBildern
aus und durchlaufen jede einzelne Datei (50).
Jedes Verzeichnis beginnt mit einem Punkt und einem Doppelpunkt. Da wir
hier keine Überprüfung vornehmen müssen, überspringen wir diese Einträge
und beenden den aktuellen Durchlauf (51). Genauso verfahren wir mit
eventuellen Unterverzeichnissen in diesem Ordner (55).
Falls es sich aber um eine Datei handelt (57), starten wir unsere Routinen
für die Überprüfung. Als Erstes überprüfen wir, wie alt die jeweilige Datei
ist (57). Wir rechnen die Zeit in Minuten um und erhalten dann die Differenz
zur aktuellen Zeit. Ist diese Zeit größer als die von uns vorgegebene Zeit in
Minuten, löschen wir die aktuelle Datei (66).
Achtung! Es findet hier keine Überprüfung der Dateiendungen statt. Falls Sie
in diesem Ordner noch andere Dateien haben, die keine CaptchaDateien
sind,
werden diese ebenfalls gelöscht!
Ist die Datei jünger als unser Wert, springen wir in den elseZweig
(76) und
vergleichen den Dateinamen mit unserem Wert (der HashWert
+ eingegebenen
Wert + Dateiendung) (62). War die Überprüfung erfolgreich, speichern wir den
Wert „TRUE“ in unsere Variable (63) – das Captcha wurde gelöst.
Wir müssen aber noch eine andere Überprüfung (65) vornehmen. Falls das
Captcha nicht gelöst wurde, müssen wir unbedingt die Datei löschen, die den
HashWert
beeinhaltet. Sie fragen sich jetzt sicherlich, warum das so sein
muss? Die Antwort ist einfach. Wenn ein Bot Ihr Formular ausfüllt und an
dem HashWert
kommt, schlägt zwar die erste Überprüfung fehl. Der Bot
könnte dann mit dem HashWert
aber alle fehlenden Kombinationen der sechs
Buchstaben oder Zahlen so lange ausprobieren, bis er das Captcha gelöst hat.
Das möchten wir natürlich unterbinden und sorgen deshalb dafür, dass das
CaptchaBild
nur einmal geprüft und danach gelöscht (66) wird.
Wir schließen das VerzeichnisHandle
(72) und geben unser Ergebnis (75 oder
77) an die aufgerufene Stelle wieder zurück.
037:
038: 039: 040: 041: 042: 043: 044: 045: 046: 047: 048: 049: 050: 051: 052: 053: 054: 055: 056: 057: 058: 059: 060: 061: 062:
063:
064: 065: 066: 067: 068: 069: 070: 071: 072: 073: 074: 075: 076: 077: 078: 079: 080: 081: 082: 083: 084: 085: 086: 087:
088:
|
function CheckCaptcha($codeCaptcha,$stringCaptcha,$dir,$delFile=5)
{
// Setzt den Check erst einmal auf FALSE $captchaTrue = FALSE; // Übergebene Hash-Variable überprüfen if(!preg_match('/^[a-f0-9]{32}$/',$codeCaptcha)) return FALSE; // Übergebene Captcha-Variable überprüfen if(!preg_match('/^[a-zA-Z0-9]{1,6}$/',$stringCaptcha)) return FALSE; $handle = @opendir($dir); while (false !== ($file = readdir($handle))){
if (preg_match("=^\.{1,2}$=", $file)){
continue; } if (is_dir($dir.$file)){
continue;
}else{
$lastTime = ceil((time() - filemtime($dir.$file)) / 60);
if($lastTime > $delFile){
unlink($dir.$file);
} else{
if(strtolower($file) == strtolower($codeCaptcha.'_'.
$stringCaptcha.'.png')){
$captchaTrue = TRUE; } if (preg_match("=^$codeCaptcha=i", $file)){
unlink($dir.$file); } } } }
@closedir($handle);
if ($captchaTrue) return TRUE; else return FALSE;
}
// Temporäres Verzeichnis der Captcha-Bilder
$captchaDir = 'captchadir/';
// Löschen der alten Captcha-Bilder nach wieviel Minuten $delFile = 10;
// Überprüfung starten $resultCaptcha = CheckCaptcha($_POST["codeCaptcha"],$_POST["stringCaptcha"],$captchaDir,$delFile); |
Beispiel 3.17: captcha_check.php
Wie Sie gesehen haben, ist eine Überprüfung möglich. Sie können jetzt das Captcha
einbinden oder erweitern. Möglich wäre die Kombination mit einer Rechenaufgabe.
Im folgenden Beispiel sehen Sie ein solches Captcha. Es erzeugt wahllos
entweder eine Rechenaufgabe oder eine Buchstaben/
Zahlenkombination.
Dieses Skript aus dem SELFPHP KOCHBUCH wurde von SELFPHP unter dem "Tarif Mc500" von McAc.net-Webhosting erfolgreich ausgeführt und getestet!
Auf der Übersichtseite unter "McAc.net – Webhosting zu diesem Buch" finden Sie weitere Informationen zu dem Webhostingpaket, dass durch SELFPHP getestet wurde.
|