Kirjautuminen

Haku

Tehtävät

Oppaat: PHP-ohjelmointi: Osa 13 - Tietoturva

  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 (2011).

Nettisivujen tietoturva-aukot ja niihin kohdistuneet hyökkäykset ovat saaneet paljon julkisuutta. Esimerkiksi syksyllä 2007 teini-ikäinen hakkeri sai käsiinsä yli 78000 suomalaisen käyttäjän salasanan hyödyntämällä alkeellisia puutteita sivustojen tietoturvassa.

Tietoturvassa on oleellista ymmärtää, mitä on tekemässä. Jos koodiin lisää "turvallisuutta" ilman ymmärrystä, ei ole mitään varmuutta siitä, että lopputulos on aidosti turvallinen. Toisaalta tuntemalla muutaman perustekniikan pystyy estämään kaikki yleisimmät tietoturva-aukot.

Käyttäjän syöte

Kriittinen kohta sivuston turvallisuudessa on, kun käyttäjä syöttää tietoa esimerkiksi lomakkeen tai sivun osoitteen kautta. Käyttäjän antama tieto voi olla mitä tahansa, minkä vuoksi siihen tulee suhtautua epäluuloisesti. Tietoturvassa oletuksena on, että käyttäjä on vihollinen.

Tarkastellaan esimerkiksi järjestelmää, jossa käyttäjät voivat lähettää toisilleen yksityisviestejä. Viestit tallennetaan tietokantaan, ja jokainen viesti saa oman id-numeronsa. Esimerkiksi järjestelmässä voi olla linkki seuraavalle sivulle, joka näyttää id-numeroa 1722 vastaavan viestin:

naytaviesti.php?id=1722

Nyt käyttäjä voi alkaa kokeilla, mitä tapahtuu, jos id-numeroa muuttaa. Seuraava sivu saattaa paljastaa toiselle käyttäjälle kuuluvan viestin, jos sivuston tietoturva ei ole kunnossa:

naytaviesti.php?id=1723

Tällaiset aukot saa estettyä tarkistamalla joka vaiheessa, että käyttäjällä on oikeus nähdä pyytämänsä tieto. Esimerkiksi jos id-numeroa 1723 vastaavan viestin vastaanottaja ei ole sama kuin kirjautuneena oleva käyttäjä, järjestelmän tulee havaita asia ja näyttää sopiva virhesivu.

SQL-injektio

SQL-injektio on yleinen tietoturva-aukko, jonka syynä on, että käyttäjän antama tieto yhdistetään sellaisenaan tietokantaan menevään SQL-kyselyyn.

Tarkastellaan esimerkiksi seuraavaa kyselyä:

SELECT id
FROM kayttajat
WHERE tunnus = '$tunnus' AND salasana = '$salasana'

Kyselyn tarkoituksena on hakea tietokannasta käyttäjän antamaa tunnusta ja salasanaa vastaava id-numero. Ongelmana on, että tunnus ja salasana yhdistetään sellaisenaan kyselyyn. Nyt käyttäjä voi antaa esimerkiksi tunnukseksi admin' -- ja salasanaksi tyhjän, jolloin kyselystä tulee seuraava:

SELECT id
FROM kayttajat
WHERE tunnus = 'admin' -- ' AND salasana = ''

Merkintä -- tarkoittaa kommenttia, joten kysely on käytännössä seuraava:

SELECT id
FROM kayttajat
WHERE tunnus = 'admin'

Käyttäjä pääsee siis kirjautumaan sisään tunnuksella "admin" tietämättä oikeaa salasanaa.

Tehokas tapa suojautua SQL-injektiolta on käyttää tietokannan käsittelyyn PDO-kirjastoa Ohjelmointiputkan oppaiden mukaisesti. Tällöin käyttäjän antamat tiedot yhdistetään kyselyihin turvallisesti eivätkä ne voi muuttaa kyselyiden rakennetta. Käytännössä PDO-kirjasto muuttaa esimerkiksi merkkijonossa olevat '-merkit muotoon \', jolloin merkkijonon lopettaminen kesken ei ole mahdollista.

XSS-aukko

XSS-aukko syntyy, kun käyttäjän lähettämä tieto tulostetaan sellaisenaan sivulle. Tällöin käyttäjä pystyy lisäämään sivulle haluamaansa HTML- tai JavaScript-koodia. Tarkastellaan esimerkiksi seuraavaa koodia:

<?php
echo "Nimi: " . $_POST["nimi"];
?>

Nyt jos käyttäjä antaa nimekseen <b>Pepe</b>, lopputulos on seuraava:

Nimi: Pepe

Tavallisin seuraus XSS-aukosta on, että käyttäjät voivat sekoittaa sivuston HTML-muotoilua (vahingossa tai tahallaan). JavaScriptin avulla myös ikävämmät seuraukset ovat kuitenkin mahdollisia. Esimerkiksi hyökkääjä voi yrittää kaapata sivun käyttäjän istunnon lisäämällä sivulle JavaScript-koodin, joka lähettää käyttäjän evästetiedot toisella palvelimella olevalle sivulle.

XSS-aukot pystyy estämään käyttämällä funktiota htmlspecialchars aina, kun sivulle tulostetaan tekstiä, jossa olevaa HTML-koodia ei ole tarkoitus käsitellä selaimessa. Funktio muuttaa käytännössä esimerkiksi merkit < ja > muotoon &lt; ja &gt;, jolloin mahdolliset tekstissä olevat tagit näkyvät sivulla sellaisenaan.

Esimerkissä tarvittava muutos on seuraava:

<?php
echo "Nimi: " . htmlspecialchars($_POST["nimi"]);
?>

Nyt jos käyttäjä antaa nimekseen <b>Pepe</b>, lopputulos on seuraava:

Nimi: <b>Pepe</b>

Salasanat

Tavallinen tapa tallentaa salasana tietokantaan on tallentaa selväkielisen salasanan sijasta salasanasta laskettu tiiviste. PHP:ssä on salasanan tiivisteen laskemiseen funktio password_hash ja tarkastamiseen funktio password_verify. Seuraavat koodit esittelevät yksinkertaisesti näiden funktioiden toimintaa:

<?php
$salasana = "selleri";
$tiiviste = password_hash($salasana, PASSWORD_DEFAULT);
echo htmlspecialchars("{$salasana} → {$tiiviste}\n");
// Yleensä tiiviste tallennettaisiin tietokantaan.
?>
<?php
$tiiviste = '$2y$10$k/Aeui2BCA6OwOwEG8XyD.mns8YuUlp5ep0xC9iJHTQnUHO9WcbPa';
// Yleensä tiiviste haettaisiin tietokannasta.

$salasana = $_POST["salasana"];
if (password_verify($salasana, $tiiviste)) {
  echo "Salasana on oikea!\n";
} else {
  echo "Salasana on väärä!\n";
}
?>

Tiivisteen tarkoituksena on, että siitä ei pysty selvittämään helposti selväkielistä salasanaa. Salasanan tarkistaminen on kuitenkin mahdollista vertaamalla oikean salasanan ja annetun salasanan tiivisteitä toisiinsa.

Ajatellaan sitten tilannetta, jossa hyökkääjä on saanut käsiinsä salasanan tiivisteen ja haluaa murtaa salasanan. Hyökkääjä voi käydä järjestelmällisesti läpi lyhyitä merkkijonoja ja verrata niiden tiivisteitä salasanaan. Hän voi myös hyödyntää tietokantoja, joihin on laskettu etukäteen suuri määrä eri merkkijonojen tiivisteitä.

Hyökkääjän työtä voi vaikeuttaa lisäämällä salasanan perään suolan ennen tiivisteen laskemista. Suola on satunnainen merkkijono, jonka tarkoituksena on pidentää salasanaa niin, ettei salasanaa voi löytää etukäteen lasketusta tietokannasta. Kun vielä jokaiselle salasanalle valitaan eri suola, hyökkääjä voi murtaa vain yhtä salasanaa kerrallaan.

Toinen lähestymistapa on hidastaa tiivisteen laskemista esimerkiksi soveltamalla tiivistefunktiota tuhat kertaa peräkkäin. Tällöin hyökkääjältä vie paljon enemmän aikaa käydä läpi mahdollisia salasanoja.

PHP:n funktio password_hash luo automaattisesti satunnaisen suolan (osaksi tiivistettä) ja toistaa varsinaista tiivistefunktiota useita kertoja. Salasanoja voi tiivistää myös esimerkiksi MD5-funktiolla, mutta silloin täytyy itse toteuttaa suolan lisäys ja funktion toisto.

Vaaralliset funktiot

Tietyt PHP:n funktiot voivat huolimattomasti käytettyinä tarjota hyökkääjälle vapaat kädet tehdä tuhoja sivustolla. Vaarallisia funktioita ovat esimerkiksi eval, joka suorittaa annetun PHP-koodin palvelimella, sekä shell_exec, joka suorittaa annetun komennon palvelimen tiedostojärjestelmässä.

Tiedoston lähettäminen selaimen kautta palvelimelle on myös vaarallinen toiminto, jos tiedostonimen tarkistus on puutteellinen. Pahimmillaan hyökkääjä pystyy lähettämään palvelimelle tiedoston, jonka päätteenä on .php ja sisältönä on mitä tahansa PHP-koodia. Aiheesta kertoo enemmän koodivinkki.

Myös tuttu include-komento saattaa olla vaarallinen:

<?php
$sivu = $_GET["sivu"];
include("yla.php");
include($sivu);
include("ala.php");
?>

Tässä ideana on, että sivun osoitteen perässä on näytettävän tiedoston nimi (esimerkiksi index.php?sivu=palaute.php). Ongelmana on kuitenkin, että osoitetta muuttamalla hyökkääjä pystyy liittämään sivun osaksi minkä tahansa palvelimella olevan tiedoston niin, että PHP-tulkki käsittelee tiedoston sisällön.


Kommentit

Deathura [13.09.2011 22:28:54]

#

muutoin kyllä erittäin, erittäin hyvä opas :D pointsit siitä, sekä kiva että koko sarja uudistui ja laajeni.

Koodi123 [08.02.2019 16:24:46]

#

Hyvä PHP-tietoturvaopas.

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 kirjoitusohjeet.
Tietoa sivustosta