Lyhyesti: Syötteet validoitte PHP:ssä funktiolla filter_var() tai filter_input_array() ja selkeillä säännöillä kenttäkohtaisesti: tyyppi, alue, pituus ja sallitut arvot. Mikä ei sovi, hylätään eikä muokata äänettömästi. Validointi on tärkeää siistien tietojen ja pienemmän hyökkäyspinnan kannalta, mutta ei korvaa SQL-injektiota vastaan valmisteltuja lauseita eikä XSS:ää vastaan asiayhteyden mukaista koodausta.
Miksi ”suodata kaikki” ei ole konsepti
Monen vanhemman PHP-projektin jokaisen tiedoston alussa on silmukka, joka ajaa kaikki $_GET- ja $_POST-arvot puhdistusfunktion läpi. Se tuntuu turvalliselta. Käytännössä se poistaa välillä liikaa, esimerkiksi heittomerkit sukunimistä, ja välillä liian vähän, koska saman funktion pitäisi toimia samanaikaisesti SQL:lle, HTML:lle ja tiedostonimille.
Järkevä validointi toimii toisin: se kysyy jokaisesta kentästä, mikä siellä on sallittua. Tilausmäärä on kokonaisluku väliltä 1 ja yläraja. Lajittelukenttä on yksi kolmesta kiinteästä arvosta. Päivämäärä on tietyssä muodossa ja toisen päivämäärän jälkeen. Kaikki muu hylätään.
OWASP erottaa tässä kaksi tasoa: syntaktisen tarkistuksen, onko arvolla oikea muoto, ja semanttisen tarkistuksen, onko se asiayhteydessä järkevä. Molemmat kuuluvat palvelimelle; selaimessa tehtävät tarkistukset palvelevat vain käytettävyyttä.
Mitä suodattimia PHP tarjoaa
Suodatinlaajennus kuuluu PHP:n vakiotoimitukseen. Tärkeimmät validointisuodattimet PHP-käsikirjan mukaan:
| Suodatin | Tarkistaa | Tärkeät asetukset ja huomautukset |
|---|---|---|
FILTER_VALIDATE_INT |
kokonaisluku | min_range, max_range |
FILTER_VALIDATE_FLOAT |
desimaaliluku | decimal erottimelle |
FILTER_VALIDATE_BOOL |
totuusarvo | asetuksella FILTER_NULL_ON_FAILURE erotettavissa arvosta ”epätosi” |
FILTER_VALIDATE_EMAIL |
sähköpostiosoitteen muoto | ei todista, että postilaatikko on olemassa |
FILTER_VALIDATE_URL |
URL-osoitteen muoto | skeema tarkistettava itse, ei kansainvälistettyjä verkkotunnuksia |
FILTER_VALIDATE_IP |
IP-osoite | liput yksityisille ja varatuille alueille |
FILTER_VALIDATE_REGEXP |
oma kuvio | ankkuroikaa kuvio merkeillä ^ ja $ |
Puhdistussuodatin FILTER_SANITIZE_STRING on vanhentunut PHP 8.1:stä alkaen. Joka käyttää sitä vielä, kannattaa siirtää validointi sopiviin suodattimiin ja käyttää tulosteessa funktiota htmlspecialchars().
filter_var PHP:ssä: lomakkeen siisti tarkistus
Funktiolla filter_input_array() koko lomakkeen voi tarkistaa yhdellä kertaa määritelmää vasten. Tuntemattomia kenttiä ei oteta mukaan:
<?php
declare(strict_types=1);
$regeln = [
'menge' => ['filter' => FILTER_VALIDATE_INT,
'options' => ['min_range' => 1, 'max_range' => 50]],
'email' => FILTER_VALIDATE_EMAIL,
'agb' => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
'versand' => ['filter' => FILTER_VALIDATE_REGEXP,
'options' => ['regexp' => '/^(standard|express)$/']],
];
$daten = filter_input_array(INPUT_POST, $regeln) ?? [];
$fehler = [];
foreach ($regeln as $feld => $_) {
if (!array_key_exists($feld, $daten) || $daten[$feld] === false || $daten[$feld] === null) {
$fehler[] = $feld;
}
}
if ($fehler !== []) {
http_response_code(422);
// Tulosta virheilmoitus kentittäin, älä käsittele syötteitä
}
Huomatkaa tiukat vertailut ===: kelvollista arvoa 0 tai tyhjää merkkijonoa ei saa sekoittaa epäonnistumiseen. Miksi löyhät vertailut ovat tässä vaarallisia, selittää Type juggling: löyhät vertailut tietoturvariskinä. Miten PHP ylipäätään tarjoaa pyyntötiedot, kuvaa Superglobalit: $_GET, $_POST, $_FILES ja muut.
Sudenkuopat sähköpostissa, URL-osoitteissa ja vapaissa teksteissä
Jotkin suodattimet tekevät vähemmän kuin niiden nimi antaa ymmärtää:
- URL-osoitteet:
FILTER_VALIDATE_URLhyväksyy myös muita skeemoja kuin http ja https. Tarkistakaa skeema sen jälkeen funktiollaparse_url()kiinteää luetteloa vasten. Jos palvelin hakee URL-osoitteen, pätevät lisäsäännöt, katso Server-Side Request Forgeryn estäminen PHP:ssä. - Sähköpostiosoitteet: Suodatin tarkistaa muodon, ei omistajuutta. Joka tarvitsee varmuuden, lähettää vahvistuslinkin.
- Vapaa teksti: Nimiä, viestejä ja osoitteita ei voi järkevästi rajata merkkien perusteella. Tarkistakaa pituus ja merkistökoodaus, esimerkiksi funktiolla
mb_check_encoding($wert, 'UTF-8'), ja luottakaa tulosteessa koodaukseen. - Säännölliset lausekkeet: Ilman merkkejä
^ja$kuvio tarkistaa vain, esiintyykö se jossain kohtaa tekstiä, ei sitä, sopiiko koko arvo. - Taulukot merkkijonojen sijaan: Hyökkääjä voi lähettää tekstin sijaan taulukon. Ilman tyyppitarkistusta se johtaa varoituksiin tai odottamattomaan käytökseen.
Kiinteille valinta-arvoille kuten lajittelulle, kielelle tai toimitustavalle sallittujen luettelo funktiolla in_array($wert, $erlaubt, true) tai match-lausekkeella on yksinkertaisempi ja tiukempi kuin mikään säännöllinen lauseke.
Toinen yleinen virhe koskee merkityksellisiä lukuja, kuten hintoja, määriä tai alennuksia. Se, että arvo on kelvollinen luku, ei kerro, onko se asiayhteydessä sallittu. Negatiivinen määrä, tavaran arvoa suurempi alennus tai hinta, joka otetaan lomakkeesta tietokannan sijaan, ovat klassisia logiikkavirheitä. Hinnat, oikeudet ja tilat kuuluvat siksi aina palvelimelle eivätkä koskaan piilotettuihin lomakekenttiin.
JSON, webhookit ja tuonnit: validointi lomakkeiden ulkopuolella
Monet syötteet eivät nykyään ole lomakkeita vaan sovellusten JSON-pyyntöjä, maksupalvelujen webhookeja tai toimittajien tuontitiedostoja. Niissäkin pätee: lukekaa ensin turvallisesti, tarkistakaa sitten. OWASP neuvoo rajaamaan koon ja sisäkkäisyyden syvyyden ennen kuin tietoja ylipäätään luetaan. PHP:ssä json_decode() enimmäissyvyydellä ja JSON_THROW_ON_ERROR-asetuksella hoitaa osan tästä:
<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // enintään 64 kt
try {
$daten = json_decode($roh, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException) {
http_response_code(400);
exit;
}
if (!is_array($daten) || !is_string($daten['bestellnummer'] ?? null)) {
http_response_code(422);
exit;
}
Webhookeissa ennen sisällön tarkistusta on kysymys, tuleeko viesti todella ilmoitetulta palvelulta. Jos palvelu tarjoaa allekirjoituksen, tarkistakaa se ja vertailkaa funktiolla hash_equals(). Tuontitiedostoissa tarkistakaa jokainen rivi kuin lomake ja keskeyttäkää virheessä siististi sen sijaan, että otettaisiin vastaan osittaisia tietueita.
Virheilmoitusten tulisi nimetä kyseinen kenttä, mutta ei palauttaa hylättyä arvoa suodattamattomana. Muuten virheilmoitukseen syntyy uusi tulostekohta, joka on koodattava uudelleen.
Mitä validointi ei voi tehdä
Validointi pienentää hyökkäyspintaa, mutta OWASPin mukaan se ei ole pääpuolustus injektiota vastaan. Heittomerkillinen sukunimi on kelvollinen ja sen on silti päästävä turvallisesti tietokantaan. Se onnistuu vain valmistelluilla lauseilla, kuten opas SQL-injektion estäminen PHP:ssä osoittaa. Ja kelvollisesta kommenttitekstistä tulee vaaratonta vasta tulosteen asiayhteyden mukaisella koodauksella.
Oikea järjestys on siis: validoi vastaanotettaessa, parametrisoi käsiteltäessä, koodaa tulostettaessa. Lisäksi kokorajat kuuluvat ennen niitä: aivan liian suuria pyyntöjä verkkopalvelimen ja PHP:n ei pitäisi edes käsitellä kokonaan. Siihen palvelevat asetukset kuten post_max_size, jotka PHP2Shellin jälkeen kannattaa joka tapauksessa tarkistaa.
Miten PHP SOS auttaa konkreettisesti?
- Kartoitamme sovelluksenne kaikki syötteet: lomakkeet, rajapinnat, uploadit, tuonnit ja webhookit.
- Määrittelemme kenttäkohtaisesti, mikä on sallittua, ja korvaamme yleiset puhdistusfunktiot selkeillä tarkistuksilla.
- Tarkistamme, ovatko tietokantakutsut ja tulosteet suojattu validoinnista riippumatta.
- Dokumentoimme säännöt niin, että tiiminne voi jatkaa niitä uusien toimintojen yhteydessä.
Järjestelmällisen tarkistuksen kuvaa palvelu tietoturva-auditointi ja tunkeutumistesti.
Usein kysytyt kysymykset
Mitä eroa on validoinnilla ja puhdistuksella?
Validointi tarkistaa, vastaako arvo odotuksia, ja hylkää sen muuten. Puhdistus muuttaa arvoa, esimerkiksi poistamalla merkkejä. Tietoturvapäätöksissä hylkääminen on lähes aina parempi, koska äänettömästi muutettu arvo voi aiheuttaa uusia virheitä.
Onko FILTER_SANITIZE_STRING vielä käyttökelpoinen?
Suodatin on merkitty vanhentuneeksi PHP 8.1:stä alkaen. PHP-käsikirja suosittelee sen sijaan funktiota htmlspecialchars() tulosteessa. Olemassa oleva koodi kannattaa muuttaa, koska vanhentuneet funktiot voivat poistua tulevista versioista.
Riittääkö FILTER_VALIDATE_URL linkkien turvaamiseen?
Ei. Suodatin tarkistaa vain osoitteen muodon. Käsikirjan mukaan kelvollisen URL-osoitteen ei tarvitse alkaa http:llä, joten skeema on tarkistettava itse sallittujen luetteloa vasten, esimerkiksi https. Kansainvälistetyt verkkotunnukset suodatin hylkää.
Pitääkö myös oman tietokannan tiedot validoida?
Tulosteessa niitä ei pidetä luotettavina, ja ne koodataan asianmukaisesti. Uusi validointi on järkevä, jos tiedot ovat alun perin tulleet ulkopuolelta ja niitä käytetään nyt uudessa yhteydessä, esimerkiksi tiedostonimenä tai komennon osana.
Auttaako validointi PHP2Shelliä vastaan?
Ei. PHP2Shell koskee pyyntöjen käsittelyä PHP-tulkissa, ennen kuin sovelluskoodinne edes toimii. Sitä vastaan auttaa vain PHP-päivitys ja kaikkien PHP-prosessien uudelleenkäynnistys.