A felhasználó által bevitt adatok érvényességét minden esetben vizsgálni kell. A kliens oldali vizsgálat (4.9.1 fejezet) a látogató számára gyorsabb, és csökkenti a szerver terheltségét, de önmagában sosem elegendő, hiszen egy rosszindulatú felhasználó azt könnyedén ki tudja játszani. Ezért az adatok szerver oldali vizsgálata is szükséges, különösen, ha az űrlapnak egy adatbázishoz kell hozzáférnie.

Mit is kaptunk?

Az egyes mezőtípusok esetén talán nem mindig triviális, hogy minek is kellene lennie az egyes szituációkban. Ezért egy egyszerű tesztelési lehetőség a print_r függvény használata:

<pre>
<?php
  print_r
($_GET);
?>

</pre>

Többes adatok

A select típusú űrlap elem és multiple tulajdonság használata esetén nem csak egy egyszerű értéket kellene visszakapunk, hanem egy tömböt. Ilyen esetben az űrlapot így érdemes felépítenünk:

<form action="form.php" method="post">
  <select name="test[]" multiple="multiple">
    <option value="one">egy</option>
    <option value="two">kettő</option>
    <option value="three">három</option>
    <option value="four">négy</option>
    <option value="five">öt</option>
  </select>
  <input type="submit" value="Küldés">
</form>

Ekkor a szerver oldalon minden adatot megkapunk, egy tömbként:

$test=$_POST['test'];
if ($test){
  foreach ($test as $t)
    echo 'Kiválasztva: ',$t,'<br>';
}

Ha get paraméterátadást használunk, az URL így fog kinézni:

form.php?test[]=two&test[]=three

Úrlapok kódolása

Ha UTF-8 karakterkódolással küldjük ki az oldalainkat, akkor célszerű az űrlapok esetén is megadni ezt az információt:

<form action="..." method="post" accept-charset="UTF-8">

Adatok érvényesítése

A felhasználótól érkező adatokban soha nem bízhatunk meg. Emiatt többféle ellenőrzést kell végeznünk a teljes biztonság érdekében.

Nézzünk meg egy egyszerű űrlapot:

<form action="myform.php" method="post">
  <p>Your Name: <input type="text" name="yourname"><br>
    E-mail: <input type="text" name="email"></p>
  <p>Do you like this website?
    <input type="radio" name="likeit" value="Yes" checked="checked"> Yes
    <input type="radio" name="likeit" value="No"> No
    <input type="radio" name="likeit" value="Not sure"> Not sure</p>
  <p>Your comments:<br>
    <textarea name="comments" rows="10" cols="40"></textarea></p>
  <p><input type="submit" value="Send it!"></p>
</form>

Ha az űrlap adatokat egyszerűen eltároljuk és/vagy később felhasználjuk, meglepetéseket tapasztalhatunk. Nézzük meg a myform.php egyszerű verzióját:

<html>
<body>
  Your name is: <?php echo $_POST['yourname']; ?><br>
  Your e-mail: <?php echo $_POST['email']; ?><br>
  <br>
  Do you like this website? <?php echo $_POST['likeit']; ?><br>
  <br>
  Comments:<br>
  <?php echo $_POST['comments']; ?>
</body>
</html>

Ekkor ilyen eredményre számítunk:

Your name is: John Doe
Your email: john@doe.com
Do you like this website? Yes
Comments:
This is my comment...

De mi történik akkor, ha a látogató egy kis JavaScript kódot ír be a beviteli mezőbe:

<script>location.href('http://www.SPAM.com')</script>

Ha ezt kiírjuk a fenti myform.php kóddal, az összes látogatót elküldjük a fenti webcímre. Ez megengedhetetlen. A következő kód a htmlspecialchars függvényt használja a támadások kivédésére:

<?php
  $yourname
= htmlspecialchars($_POST['yourname']);
 
$email    = htmlspecialchars($_POST['email']);
 
$likeit   = htmlspecialchars($_POST['likeit']);
 
$comments = htmlspecialchars($_POST['comments']);
?>

<html>
<body>
  Your name is: <?php echo $yourname; ?><br>
  Your e-mail: <?php echo $email; ?><br>
  <br>
  Do you like this website? <?php echo $likeit; ?><br>
  <br>
  Comments:<br>
  <?php echo $comments; ?>
</body>
</html>

Ekkor a fenti JavaScript kódból ez a közömbösített kód lesz:

&lt;script&gt;location.href('http://www.SPAM.com')&lt;/script&gt;

Ezen kívül további függvények is szóba jöhetnek az adatok megtisztítására. Ezek egységes használatára nézzünk egy példát:

function check_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
}
$yourname = check_input($_POST['yourname']);
$email    = check_input($_POST['email']);
...

Kötelező mezők kezelése

Fejlesszük tovább a check_input függvényt. Ha egy mezőt kötelezően ki akarjuk töltetni, akkor ezt az opciót egy második paraméterrel beépíthetjük a függvénybe: ha átadjuk a szöveges paramétert, akkor ezzel jelezzük, hogy kötelező a kitöltés, és ekkor ez lesz a kiadandó hibaüzenet is:

function check_input($data, $problem='') {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    if ($problem && strlen($data) == 0)     {
        die($problem);
    }
    return $data;
}

A következő felhasználás esetén az első és negyedik mező kitöltése kötelező, a középsőké nem:

$yourname = check_input($_POST['yourname'], "Enter your name");
$email    = check_input($_POST['email']);
$likeit   = check_input($_POST['likeit']);
$comments = check_input($_POST['comments'], "Write your comments");

Persze a hibaüzenetek lekezelésére (die függvény) ez csak egy első megoldás, tovább kell fejlesztenünk.

Reguláris kifejezések

A szintaxis-ellenőrzés régi bevált módszere a reguláris kifejezések használata. Itt most néhány példát fogunk megnézni. A hangsúly nem a reguláris kifejezések működésén, hanem azok PHP felhasználásán lesz.

Az e-mail cím ellenőrzésére egy lehetséges megoldás:

$email = htmlspecialchars($_POST['email']);
if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email)) {
    die("E-mail address not valid");
}

URL ellenőrzése:

$url = htmlspecialchars($_POST['website']);
if (!preg_match("/^(https?:\/\/+[\w\-]+\.[\w\-]+)/i",$url)) {
    die("URL address not valid");
}

Számjegyek:

if (preg_match("/\D/",$age)) {
    die("Please enter numbers only!");
}

Angol abc betűi:

if (preg_match("/[^a-zA-Z]/",$text)) {
    die("Please enter letters a-z and A-Z only!");
}

Űrlapok feldolgozása helyben

Az adatok ellenőrzésére bevett módszer, hogy az űrlap adatait az űrlapot is tartalmazó szkript dolgozza fel, így nincs szükség arra, hogy az adatokat egy újabb oldalra küldjük. Ebben az esetben a felhasználó a hibaüzeneteket ugyanazon az oldalon láthatja, ahol az űrlap található, így megkönnyítve a hiba felfedezését és kijavítását.
Nézzük az alapokat. Legyen az oldal neve form-action.php.

<?php
 
if(isset($_POST['submit'])) {
   
$name = $_POST['name'];
    echo
"User Has submitted the form and entered this name : <b> $name </b>";
    echo
"<br>You can use the following form again to enter a new name.";
}
?>

<html>
<head><title>Using PHP_SELF</title></head>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
  <input type="text" name="name"><br>
  <input type="submit" name="submit" value="Submit Form"><br>
</form>
</body>
</html>

Először is meglepő, hogy nem az űrlappal kezdjük a forráskódot. Itt a háttérben már körvonalazódik az a – később egyre fontosabbá váló – logika, ami szerint a lehető legjobban válasszuk szét a program logikáját a megjelenítési részektől.

A kód elején el kell döntenünk, hogy most először akarja látni a látogató az oldalt, és üres űrlappal kell várnunk, vagy pedig az űrlap már kitöltve érkezett vissza a látogatótól. Az első esetben nem fog semmilyen POST adat érkezni. A második esetben viszont igen: a Submit Form gomb lenyomásakor már a POST adatok is elküldésre kerültek. Emiatt az isset függvény alkalmas a két eset megkülönböztetésére.

A form action paraméterét szokás a fenti módon kitölteni. A $_SERVER['PHP_SELF'] ugyanannak az oldalnak a címe, amely a fenti PHP kódot tartalmazza.

Itt is számítanunk kell azonban egy betörési lehetőségre. Ha a látogató a böngészője cím sorába beírja a következő címet, a $_SERVER['PHP_SELF'] változó egy ügyesen elhelyezett támadókódot tartalmaz.

A PHP futás eredménye ekkor:

<form name="test" method="post" action="form-action.php"/>
<script>alert('xss')</script><foo"">

Az XSS támadás elkerülése érdekében inkább így használjuk:

<form name="test"
  action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>"
  method="post">

Hibaüzenetek és javítási lehetőségek

A látogatók sokszor nem tudják hiba nélkül kitölteni az űrlapot. Emiatt fontos az alábbi elvek figyelembe vétele:

  1. az űrlap eleve tartalmazzon minden információt, ami a kitöltéssel kapcsolatos
  2. kliens oldalon ellenőrizzük az adatokat JavaScript segítségével
  3. beszédes hibaüzenetekkel lássuk el a látogatót, a hiba helyét könnyen beazonosítható módon
  4. adjuk vissza a látogatónak a megadott adatait, hogy ne kelljen mindent elölről kezdenie

A korábbi példáink a hibaüzeneteket eléggé mostohán kezelték. Többször használtunk olyan kódot, amely az első hibánál leáll. De a használhatóság miatt inkább össze kell gyűjtenünk a hibaüzeneteket, és a megfelelő helyen kiírni.

Nézzünk egy minimális példát a hibaüzenetek összegyűjtésére. A változók az űrlapról érkező adatokat tartalmazzák:

$errorMessage = '';
if(empty($varMovie)) {
  $errorMessage .= "<li>You forgot to enter a movie!</li>";
}
if(empty($varName)) {
  $errorMessage .= "<li>You forgot to enter a name!</li>";
}
if(empty($varGender)) {
  $errorMessage .= "<li>You forgot to select your Gender!</li>";
}

Jól látszik, hogy a futás végére az $errorMessage vagy üres marad, és ekkor nem volt probléma, vagy nem marad üres, és felsorolásként tartalmazza a hibaüzeneteket, és már csak ki kell írnunk egy ul elembe. Még szebb megoldás lenne, ha közvetlenül a mező mellett jelenne meg a hibaüzenet. (A 3.10.1 fejezetben látni fogunk erre is példát.)

Ha az űrlap feldolgozása során azt láttuk, hogy nem tökéletes, és újból a látogató felé kell továbbítani, akkor vissza kell küldenünk a korábban kitöltött adatokat. Erre egy egyszerű példa:

<input type="text" name="Coal"
  <?php if (isset($_POST['Coal'])) { ?>
    value="<?php echo $_POST['Coal']; ?>"
  <?php } ?>
/>

Ha a látogató már kitöltötte a Coal mezőt, az isset($_POST['Coal']) igaz lesz, és kiírjuk a value értékeként.

Érdemes azt is átgondolni, hogy az if-nek inkább olvashatósági jelentősége van, nélküle is megfelelő lenne a kimenet:

  • ha a feltétel teljesülne, akkor nincs változás,
  • ha a feltétel nem teljesül, akkor a value üres értékkel jelenik meg.