Kirjautuminen

Haku

Tehtävät

Oppaat: PHP-ohjelmointi: Osa 18 - PHP:n ongelmat

  1. Osa 1 - Johdanto
  2. Osa 2 - Muuttujat
  3. Osa 3 - if-rakenne
  4. Osa 4 - for-silmukka
  5. Osa 5 - Taulukot
  6. Osa 6 - Lomakkeet
  7. Osa 7 - Nettisivusto
  8. Osa 8 - Lisää silmukoista
  9. Osa 9 - Tiedostot
  10. Osa 10 - Omat funktiot
  11. Osa 11 - Istunnot
  12. Osa 12 - Tietokannat
  13. Osa 13 - Tietoturva
  14. Osa 14 - Olio-ohjelmointi
  15. Osa 15 - Kuvien luonti
  16. Osa 16 - Säännölliset lausekkeet
  17. Osa 17 - Merkistöt
  18. Osa 18 - PHP:n ongelmat

Kirjoittaja: Antti Laaksonen. Vuosi: 2011.

Kaikissa ohjelmointikielissä on omat hyvät ja huonot puolensa. Vaikka PHP:ssä on paljon hyvää, siinä on myös asioita, jotka aiheuttavat ohjelmoijille usein harmaita hiuksia ja joista on hyvä olla tietoinen etukäteen.

Jotkut ohjelmoijat suhtautuvat PHP-kieleen hyvin kielteisesti. Toisinaan taustalla on ylimielinen asenne: kansan syvät rivit käyttävät PHP:tä, joten se ei voi olla hyvä. Toisaalta osa PHP:n saamasta kritiikistä on myös oikeutettua.

Perusongelma ohjelmointikielen kehittämisessä on, että kerran valittua suunnitteluratkaisua voi olla vaikeaa muuttaa myöhemmin. Myös PHP:ssä monet ongelmat ovat historiallisia, ja jos kielen saisi suunnitella uudestaan puhtaalta pöydältä, ne voisi korjata helposti. Näin ei voida kuitenkaan tehdä, koska olemassa olevat PHP-sovellukset luottavat kielen nykyisiin piirteisiin eivätkä toimisi kielen uudistuksen jälkeen.

PHP:n rajat

Onko olemassa asioita, joita ei voi tehdä PHP:llä vaan on pakko siirtyä toiseen kieleen? Ohjelmoinnin teorian näkökulmasta vastaus on yksinkertainen: PHP pystyy kaikkeen samaan kuin muutkin kielet.

Syynä tähän on, että PHP on Turing-täydellinen kieli. Turingin kone on yksinkertainen tietokoneen malli, joka koostuu muistinauhasta ja sitä käsittelevästä laitteesta. Ohjelmointikieli on Turing-täydellinen, jos sillä voi simuloida Turingin konetta. PHP:ssä tämä onnistuu helposti muutaman kymmenen rivin koodilla, joten PHP on Turing-täydellinen. Kiinnostava asia on, että tähän mennessä ei ole kehitetty ohjelmointikieltä, jonka laskentakyky ylittäisi Turingin koneen. Niinpä PHP on samalla viivalla muiden kielten kanssa.

Tyyppimuunnokset

PHP:ssä tiedon tyyppi on häilyvä käsite, koska PHP tekee runsaasti automaattisia tyyppimuunnoksia. Esimerkiksi seuraavassa koodissa muuttujat $a ja $b ovat merkkijonoja, mutta ne muuttuvat automaattisesti luvuiksi kertolaskussa.

<?php
$a = "5";
$b = "7";
echo $a * $b;
?>

Tyyppien huoleton käsittely ei ole yksiselitteisesti hyvä tai huono asia. Toisaalta se mahdollistaa joustavan ohjelmoinnin, kun muuttujan sisältö tulkitaan tilanteesta riippuen sopivalla tavalla. Välillä kuitenkin automaattisten tyyppimuunnosten seuraukset voivat olla odottamattomia.

Funktio strpos tarjoaa hyvän esimerkin tyyppimuunnosten ongelmista. Funktion tarkoituksena on kertoa, missä kohtaa merkkijonoa toinen merkkijono esiintyy ensimmäisen kerran. Esimerkiksi kutsu strpos("esimerkki", "me") tuottaa arvon 3, koska merkkijono "me" esiintyy kohdassa 3 merkkijonossa "esimerkki".

Jos merkkijonon esiintymää ei ole, strpos palauttaa arvon false. Seuraava koodi vaikuttaa siis toimivalta:

<?php
if (strpos("esimerkki", "aapeli") == false) {
    echo "Merkkijonoa ei löytynyt!";
}
?>

Seuraava esimerkki paljastaa kuitenkin koodissa olevan ongelman:

<?php
if (strpos("esimerkki", "esi") == false) {
    echo "Merkkijonoa ei löytynyt!";
}
?>

Vaikka merkkijono "esi" esiintyy merkkijonossa "esimerkki", koodi tulostaa "Merkkijonoa ei löytynyt!" Ongelmana on, että "esi" esiintyy heti merkkijonon alussa eli kohdassa 0. PHP tulkitsee kuitenkin, että luku 0 vastaa totuusarvoa false, minkä vuoksi ehto pitää paikkansa.

Ratkaisu ongelmaan on käyttää vertailussa merkintää ===, joka vaatii, että verrattavat arvot ovat myös tyypiltään samat. Tämän muutoksen jälkeen koodin toiminta on oikea:

<?php
if (strpos("esimerkki", "esi") === false) {
    echo "Merkkijonoa ei löytynyt!";
}
?>

Funktiokirjasto

PHP sisältää paljon hyödyllisiä funktioita, mutta funktiokirjastoa ei voi pitää huolellisesti suunniteltuna. Ongelmia ovat:

Toisin kuin useimmissa nykyaikaisissa kielissä PHP:ssä kaikki funktiot ovat suoraan käytettävissä. Esimerkiksi satunnaisen luvun voi arpoa seuraavasti:

<?php
echo rand(1, 10);
?>

Python-kielessä vastaava funktio on kirjastossa random, josta se täytyy ottaa erikseen käyttöön import-rivillä:

import random
print random.randint(1, 10)

Monien mielestä funktioiden jakaminen erillisiin kirjastoihin olisi selkeämpi ratkaisu kuin PHP:n käytäntö.

Virheilmoitukset

Seuraava PHP-koodi ei toimi oikein, koska koodin alussa muuttujan nimenä on virheellisesti $etunini eikä $etunimi:

<?php
$etunini = "Aapeli";
$sukunimi = "Koodinen";
echo $etunimi;
echo $sukunimi;
?>

Vaikka muuttujaa $etunimi ei ole olemassa, tämä ei keskeytä koodin suoritusta. PHP:n asetukset määrittävät, tuleeko puuttuvasta muuttujasta virheilmoitusta. Jos virheilmoitukset ovat käytössä, sivulle tulee seuraava ilmoitus määrittelemättömästä muuttujasta:

Notice: Undefined variable: etunimi in testi.php on line 4

Kuitenkin jos virheilmoitukset on piilotettu, ainoa seuraus kirjoitusvirheestä muuttujan nimessä on, että ensimmäinen echo-komento ei tulosta mitään.

PHP:n virheilmoitukset kannattaa pitää käytössä koodin toteutusvaiheessa, koska muuten monien virheiden korjaaminen ja jopa huomaaminen on vaikeaa. Virheilmoitukset saa yleensä näkyviin kirjoittamalla koodin alkuun seuraavat rivit:

<?php
ini_set("display_errors", 1);
ini_set("error_reporting", E_ALL | E_STRICT);
// koodi tulee tähän
?>

Rivit muuttavat tiedoston suorituksen ajaksi PHP:n asetuksia display_errors ja error_reporting, jotka vaikuttavat virheilmoitusten näyttämiseen. Asetuksia pystyy myös muuttamaan pysyvästi tiedostosta php.ini, jos tiedoston muuttaminen on mahdollista.

Suorat muuttujat

PHP:n alkuaikoina taulukot $_GET, $_POST, $_COOKIE ja vastaavat olivat tuntemattomia. Niiden sijasta esimerkiksi lomake- ja evästetiedot olivat suoraan käytettävissä samannimisinä muuttujina.

Seuraavassa on sama koodi kirjoitettuna vanhalla ja uudella tyylillä:

<?php
// vanha tyyli
echo "Hei {$nimi}, ikäsi on siis {$ika} vuotta!";
?>
<?php
// uusi tyyli
$nimi = $_GET["nimi"];
$ika = $_GET["ika"];
echo "Hei {$nimi}, ikäsi on siis {$ika} vuotta!";
?>

Vanhan tyylin ongelmana on, että hyökkääjä pystyy määrittelemään mielensä mukaan PHP:n muuttujia. Tämä voi aiheuttaa tietoturvaongelman, jos tärkeitä muuttujia ei määritellä koodissa kaikissa tilanteissa. Tarkastellaan esimerkiksi seuraavaa tiedostoa vaara.php:

<?php
include("funktiot.php");
if (sisalla()) {
    $kayttaja = hae_kayttaja();
}
if (isset($kayttaja)) {
    echo "salainen sisältö";
}
?>

Ideana on, että muuttuja $kayttaja sisältää kirjautuneen käyttäjän nimen. Kuitenkin hyökkääjä voi mennä sivulle vaara.php?kayttaja=aapeli, jolloin muuttujan $kayttaja sisällöksi tulee "aapeli" riippumatta funktioista sisalla ja hae_kayttaja.

Taulukoiden käyttäminen samannimisten muuttujien sijasta on ollut jo pitkään yleinen käytäntö PHP-ohjelmoinnissa. Kuitenkin joillakin palvelimilla samannimisiä muuttujia voi käyttää edelleen, koska jotkin vanhat PHP-koodit olettavat niiden toiminnan. PHP:n asetus register_globals säätelee, voiko samannimisiä muuttujia käyttää.

Automaattiset kenoviivat

Opassarjan osassa 13 esitelty SQL-injektio on tyypillinen PHP-koodissa oleva tietoturvaongelma. Aiemmin PHP:ssä oli usein käytössä ominaisuus, joka pyrki estämään SQL-injektion automaattisesti lisäämällä lomaketietoihin kenoviivoja.

Ominaisuudesta on hyötyä esimerkiksi seuraavassa koodissa:

<?php
$nimi = $_GET["nimi"];
$sql = "SELECT * FROM kayttajat WHERE nimi = '$nimi'";
// jne.
?>

Koodissa näyttää päältä päin olevan SQL-injektion mahdollisuus, koska lomakemuuttujan nimi arvo tulee sellaisenaan osaksi kyselyä. Kuitenkin jos automaattiset kenoviivat ovat käytössä, jokaisen muuttujassa olevan heittomerkin eteen ilmestyy automaattisesti kenoviiva, mikä estää SQL-injektion. Esimerkiksi jos muuttujan arvona on Aap'eli, se muuttuu automaattisesti muotoon Aap\'eli.

Vaikka automaattiset kenoviivat voivat estää turvallisuusaukkoja ohjelmoijan huomaamatta, ominaisuus aiheuttaa myös ongelmia. Jos esimerkiksi koodi tulostaa lomakemuuttujan sisällön suoraan sivulle, siinä saattaa näkyä häiritseviä ylimääräisiä kenoviivoja. Joillakin palvelimilla automaattiset kenoviivat voivat olla edelleen käytössä, jolloin nykyaikainen koodi ei toimi oikein. PHP:n asetus magic_quotes_gpc säätelee, ovatko automaattiset kenoviivat käytössä.


Kommentit

ErroR++ [04.07.2012 20:59:04]

Lainaa #

Antti Laaksonen kirjoitti:

Esimerkiksi funktiossa str_replace alkuosan str jälkeen on alaviiva mutta funktiossa strtoupper ei ole alaviivaa.

Taitaa johtua siitä että strreplace:ssa olisi kaksi ärrää peräkkäin eikä se vaikuta järkevältä. str_replace on myös helpommin luettava.

Antti Laaksonen [17.07.2012 19:07:41]

Lainaa #

Hyvä pointti, mutta esimerkiksi funktiossa str_shuffle on alaviiva ja funktiossa strstr ei ole, vaikka molemmissa etuliitteen jälkeen tulee kirjain s.

Hengilö [17.06.2013 20:06:00]

Lainaa #

Antti Laaksonen kirjoitti:

ErroR++ kirjoitti:

Antti Laaksonen kirjoitti:

Esimerkiksi funktiossa str_replace alkuosan str jälkeen on alaviiva mutta funktiossa strtoupper ei ole alaviivaa.

Taitaa johtua siitä että strreplace:ssa olisi kaksi ärrää peräkkäin eikä se vaikuta järkevältä. str_replace on myös helpommin luettava.

Hyvä pointti, mutta esimerkiksi funktiossa str_shuffle on alaviiva ja funktiossa strstr ei ole, vaikka molemmissa etuliitteen jälkeen tulee kirjain s.

Menee vähän offtopicin puolelle, mutta tässä puhuttiinkin r-kirjaimesta, ei ässästä.

Metabolix [17.06.2013 20:44:52]

Lainaa #

Hengilö, oppaassa taas ei puhuta r-kirjaimesta, joten vaikka ErroR++ on keksinyt yhdelle alaviivalle selityksen, muissa PHP:n funktioissa vastaava selitys ei päde, kuten Antin esimerkki osoittaa. Sitä paitsi onhan olemassa myös r-kirjaimellisia vastaesimerkkejä kuten strrev ja strripos.

Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea keskustelun ohjeet.
Tietoa sivustosta