Haluaisin tehdä (oppia tekemään) "oikeanlaisen" ja tietoturvallisen kommentointilomakkeen, jossa käyttäjä kirjoittaa vain nimen ja kommentin. Tiedot tallennetaan MySQL-tietokantaan.
vieraskirja.php :
<form method=post action="vieraskirja_tallennettu.php"> NIMESI:<input type=text name="nimi" size="15" maglength="30"><br><br> KIRJOITA VIESTISI: <textarea style="background-color: #CBD7FE; border: 2px solid #003366;" name="viesti" cols="50" rows="4"></textarea><br><br> <input type=submit name="toiminto" value="LÄHETÄ VIESTI"> </form> <?php //Tietokantayhteyden luonti sensuroitu if (!$haeviestit = mysql_query("SELECT nimi, viesti, DATE_FORMAT(lisatty, '%d.%m.%Y' ' %T') AS aika FROM Vieraskirja ORDER BY lisatty DESC",$dbh)) { print "Viestien listaaminen epäonnistui."; } else { while($row = mysql_fetch_assoc($haeviestit)) { print "<br>" . $row['viesti']; print "<p class=pieniteksti>Kirjoitti: " . $row['nimi']; print " Aika: " . $row['aika']; print "<br>--------------------------------------------------------------------------------<br>"; } } ?>
vieraskirja_tallennettu.php :
<?php //Tietokantayhteyden luonti sensuroitu $sivunotsikko; $viestiteksti; $sql_lauseke = "insert into Vieraskirja (nimi,viesti,lisatty) values ('$nimi','$viesti',NOW()) "; if (!$haeviestit = mysql_query($sql_lauseke,$dbh)) { $sivunotsikko = "Viestin tallennus epäonnistui! "; $uutisteksti = "Virhe: " . mysql_error(); } else { $sivunotsikko = "Viestin tallennus onnistui!"; $viestiteksti = "Seuraavanlainen viesti talletettu:<p>"; $viestiteksti .= "Kirjoittaja: " . $nimi . "<br>"; $viestiteksti .= "Uutinen: " . $viesti. "<br>"; } ?> <html> <head> <title><?php print "$sivunotsikko"; ?></title> </head> <body bgcolor="#CCCCCC"> <h2><?php print "$sivunotsikko"; ?></h2> <?php print "$viestiteksti"; ?> JNE...
Onko toi ihan ok noin vai onko paljon helpompia tapoja?
Miksi aina puhutaan esim. tästä post-muuttujasta: $_POST
Koska sitä pitäisi käyttää?
Missä voidaan käyttää tätä "$_GET"
Kun joka puolella hoetaan, että se näyttää osoiterivillä kaikki tiedot, joten se on tietoturvauhka!
Minun pitäisi varmastikin seuraavanlainen tarkistus lisätä:
if ($_SERVER["HTTP_REFERER"]=="http://www.minunsivut.fi/vieraskirja.php"){//käsitellään lomake} else (print "Tiedot tulevat virheellisestä osoitteesta. Lomakkeen käsittely lopetetaan.");
Mihin kohtaan tuo?
Ja mitä eroa "print" ja "echo" käskyillä?
Tuossa nyt muutama kysymys. Kyllä, olen aloittelija php:n koodauksessa :)
Nopea selitys noista metodeista (tästä voisi kirjoittaa vaikka artikkelin):
POST ja GET ovat tapoja kuljettaa sitä tietoa webbisivulta toiselle. GET-metodilla nuo tiedot kulkevat osoiterivillä ja GET-metodi on oiva tapa ohjata esim. sivujen toiminnallisuutta. Esim linkkien avulla:
index.php?action=show_messages edit_message.php?id=2
jolloin noilla sivuilla löytyy seuraavanlaiset muuttujat GET-muuttujat
$_GET['action'] ja $_GET['id']
ja joiden arvot ovat 'show_messages' ja 2
POST-metodia voidaan käyttää vain lomakkeissa ja se soveltuu suurempien tietomäärien kuljettamiseen sivulta toiselle. Siinä missä osoiteriville mahtuu vain 255 merkkiä, sivunpyynnön yhteydessä voi POST-metodia käyttäen lähettää vaikka yhden kirjan verran tekstiä.
Eli siis jos lomakkeessa on method="post", niin silloin ne lomakkeen kentät löytyväl vastaanottavalta sivulta $_POST-taulukosta. Esim. tekstikentän
<input type="text" name="foo" />
tiedot löytyy muuttujasta
$_POST['foo']
Kiitos. Mutta "GET" komentoa ei käytetä "lomakeasioissa" koska se on usein tietoturvauhka. Eikö?
Mitä pidät tuosta minun koodistani, onko ihan "ok"?
Lähinnä haluaisin tietää että mitä eroa tuolla minun toteutustavalla on siihen jos käyttäisin "$_POST" muuttujia?
Tässä tulee hieman päällekkäisyyksiä ajv:n viestin kanssa:
GET- ja POST-metodin erona on tiedon välitystapa. GET-tieto kulkee selaimen osoiterivillä näkyvän sivun osoitteen mukana ja POST-tieto sivun käyttäjälle näkymättömien otsikkotietojen mukana. Kumpikaan metodi ei ole kuitenkaan toista turvallisempi, koska tietoa ei salata mitenkään.
GET-metodi on tarkoitettu lähinnä sellaisiin tapauksiin, joissa palvelimelta vain haetaan tietoa. Esim. tässä keskustelusivussa näkyy osoitteessa tunnus=11573, joka ilmoittaa PHP-skriptille, mikä viestiketju pitää näyttää. GET-lomake ei siis yleensä tee muutoksia palvelimen tiedostoihin tai tietokantaan.
POST-metodin käyttöalue on laajempi. Koska tietoa ei tarvitse ahtaa sivun osoitteeseen, POST-tietoa voi olla erittäin paljonkin. Tämän sivun viestinkirjoituslomake on POST-lomake, jonka kohteena oleva PHP-skripti lisää tietokantaan yhden uuden viestin. Yksi muistisääntö on muuten metodien nimet - GET on "haku" ja POST "lähetys".
PHP-skripin toiminta on samanlainen välitystavasta riippumatta. Ainoa ero on siinä, että GET-metodin kanssa käytetään $_GET-taulukkoa ja POST-metodin kanssa $_POST-taulukkoa. Jos PHP:n asetuksissa on määritetty, että lomakemuuttujia voi käsitellä suoraan ilman erillisiä taulukoita, sekä GET- että POST-tiedot ovat samannimisinä muuttujina. Silloin PHP-skriptiin ei tule siis mitään muutoksia välitystavan vaihtuessa.
Sinun skriptisi näyttää ihan hyvältä. Varmista vielä, että lomakemuuttujiin lisätään automaattisesti kenoviivat (\) ennen heittomerkkejä. Muuten kenoviivat täytyy lisätä itse mysql_escape_string-funktiolla. Kenoviivat täytyy lisätä tietokantaan tallennettaessa sen takia, että muuttujien mahdolliset heittomerkit eivät sotkeennu SQL-kielen heittomerkkeihin.
Nämä PHP:n asetukset liittyvät lomakkeisiin:
- register_globals (lomakemuuttujat suoraan samannimisinä muuttujina tai erillisissä taulukoissa)
- magic_quotes_gpc (lisätäänkö lomakemuuttujiin automaattisesti SQL-kyselyissä tarvittavat kenoviivat)
Kiitos kaima. Menee vain hermot kun näitä tapoja näyttää olevan miljoona erilaista. Yritän siis omaksua sitä "helpointa" tapaa. Hmm, siis kuitenkin tuo koodini toimii niin miksi lisätä kenoviivat?
Ja sitten olen oppaista ymmärtänyt, että jotenkin POSTin käyttäminen muuttujissa on tietoturvallisempaa...
Tai mitä väliä sillä on kun minulla on "$row" eikä "$post"?
$sql_lauseke="INSERT INTO Vieraskirja (id,nimi,viesti,lisatty) VALUES('".$id."','".$_POST['nimi']."','".$_POST['viesti']."')"; mysql_query($sql_lauseke,$dbh) or die ("viestiä ei pystytty kirjaamaan");
Se olisi jotenkin noin POSTilla!?
Hmm... Minun koodista puuttuu tuo ID, mutta silti toimii oikein. Argh, nyt jäähylle :)
Ja kun en käytä $POSTia niin en voi tarkastaa onko lomakkeen kenttä tyhjä?
Antti80 kirjoitti:
Hmm, siis kuitenkin tuo koodini toimii niin miksi lisätä kenoviivat?
Oletko kokeillut koodin toimintaa siinä tapauksessa, että viestissä on heittomerkki? SQL-kyselyssä on kohta '$viesti'
. Jos $viesti olisi a 'b' c
, kysely tulisi muotoon 'a 'b' c'
. Nyt merkkijono päättyy virheellisesti liian aikaisin. Siksi $viesti pitää muuttaa muotoon a \'b\' c
, jolloin heittomerkit eivät sekoitu SQL-kyselyn heittomerkkeihin. Tarvittavat kenoviivat lisätään automaattisesti kaikkiin lomakemuuttujiin, jos magic_quotes_gpc-asetus on päällä.
Antti80 kirjoitti:
Ja sitten olen oppaista ymmärtänyt, että jotenkin POSTin käyttäminen muuttujissa on tietoturvallisempaa.
Tarkoitatko, että $_POST['viesti'] on turvallisempi kuin pelkkä $viesti? Paljon puhuttu tietoturvariski on todellisuudessa harvinainen erikoistapaus:
<?php if ($tunnus == "Antti" && $salasana == "123") { $paasy = true; } if ($paasy) { echo "Tämä on salainen sivu."; } ?>
Tässä huonosti tehdyssä PHP-skriptissä on aukko. Skriptin tekijä on olettanut, että muuttuja $paasy on false, jos sitä ei erikseen määritellä. Mutta koska lomakemuuttujat ovat samannimisinä muuttujina, käyttäjä voi kirjoittaa osoitteeksi sivu.php?paasy=1. Nyt salainen tieto tulee näkyviin ilman oikeaa tunnusta ja salasanaa.
Siis $_GET- ja $_POST-taulukoiden käyttäminen on turvallisempaa vain silloin, jos skripti on erittäin huonosti tehty. Nykyään kuitenkin monilla palvelimilla suorat muuttujat eivät toimi, minkä vuoksi on paras alistua käyttämään taulukoita yhteensopivuuden nimissä.
Antti80 kirjoitti:
Ja kun en käytä $POSTia niin en voi tarkastaa onko lomakkeen kenttä tyhjä?
Sen voi tarkistaa vaikka näin:
if ($viesti == "") echo "Viesti puuttuu!";
Taulukoiden kanssa:
if ($_POST['viesti'] == "") echo "Viesti puuttuu!";
Ymmärsit kysymykseni juuri oikein, nyt selvisi moni asia. Kiitos! Yritän kyllä tämän tehdä sitten käyttäen tuota $_POST muuttujaa.
>jos magic_quotes_gpc-asetus on päällä.
Onko toi kotisivutilan tarjoajan palvelin asetus, että jos se on päällä niin minun ei tarvitse asiasta murehtia vai pitääkö minun aina tuo itse koodata. tai jäättää koodamatta mutta kirjoittaa käsin aina kenoviivat?
Vielä jäi vähän jumittamaan tuo, että mikä ero on taulukolla. Tai kun annoit tohon tarkistukseen 2 eri tapaa.
Ja kun näitä if-lauseita tulee niin monta (?) niin olen aika pihalla että miten ne laitetaan järkevästi itse koko "skriptin" sekaan... :|
Se tyhmä lisäys vielä, että eihän tuo $row ole mikään erikoisuus, siis että kyse on aina muuttujasta jonka itse on määrittänyt eli se voi olla vaikka "$jeesusajaamopolla"!? :) Mutta kuitenkin tuo $POST on joku virallinen, siis sillä on oikea merkitys ja käytäntö?
Antti80 kirjoitti:
Onko toi kotisivutilan tarjoajan palvelin asetus, että jos se on päällä niin minun ei tarvitse asiasta murehtia vai pitääkö minun aina tuo itse koodata.
Asetus on tosiaan palvelimen ylläpitäjän säädettävissä. Voit tarkistaa asetuksen tällä skriptillä:
<?php echo get_magic_quotes_gpc(); // 1 = automaattiset kenoviivat ?>
Jos magic_quotes_gpc on päällä, lomaketietoja voi siirtää suoraan tietokantaan huolehtimatta kenoviivoista. Muussa tapauksessa SQL-kyselyyn liitettäviin lomakemuuttujiin täytyy itse lisätä kenoviivat mysql_escape_string-funktiolla.
Toisaalta jos magic_quotes_gpc on päällä, lomakemuuttujissa on ylimääräisiä kenoviivoja, jos niitä ei siirretäkään tietokantaan. Tällainen tilanne on silloin, kun lomakkeen tiedot tallennetaan tiedostoon tai näytetään suoraan sivulla. Silloin ylimääräiset kenoviivat saa pois stripslashes-funktiolla.
Antti80 kirjoitti:
Vielä jäi vähän jumittamaan tuo, että mikä ero on taulukolla. Tai kun annoit tohon tarkistukseen 2 eri tapaa.
Jos register_globals-asetus on päällä, lomaketiedot kopioituvat kahteen paikkaan: samannimiseen muuttujaan ja taulukkoon. Jos asetus ei ole päällä, lomaketiedot siirtyvät ainoastaan taulukkoon. Tämä on juuri taulukoiden etu: ne toimivat register_globals-asetuksesta riippumatta.
Antti80 kirjoitti:
Se tyhmä lisäys vielä, että eihän tuo $row ole mikään erikoisuus, siis että kyse on aina muuttujasta jonka itse on määrittänyt eli se voi olla vaikka "$jeesusajaamopolla"!? :)
Juuri näin. Tässä tapauksessa $row-taulukkoon tulee vuorollaan jokainen tietokannasta haettu rivi. Kaikki rivit käydään läpi while-silmukalla. Tällä ei kuitenkaan ole mitään tekemistä lomaketietojen kanssa. Ainoa yhteinen piirre on se, että $_POST ja $row ovat kummatkin taulukkoja.
Tuli "1", eli on automaattiset kenoviivat. Hyvä ettei siitä tarvitse sitten murehtia.
Eli mielestäsi tuo minun eka koodini on ihan toimiva, eikä ole mitään suoranaisia tietoturvariskejä?
Osaisitko kuitenkin kertoa vielä, että miten/mihin tämän lisään:
if ($_SERVER["HTTP_REFERER"]=="http://www.minunsivut.fi/vieraskirja.php"){//käsitellään lomake} else (print "Tiedot tulevat virheellisestä osoitteesta. Lomakkeen käsittely lopetetaan.");
ja entä tämä:
if ($_POST['viesti'] == "") echo "Viesti puuttuu!";
Mutta jos lisään tuon tohon ekaan koodiini, niin $_post pitää muuttaa "$row" ?
Pitänee ajan kanssa ja vielä oppaita lukien koittaa tehdä sitten kokonaan tuo uusiksi käyttäen tuota $_POST.
Hmm... sori, mutta vieläkin jumitan. Siis onko $_POST mikään erikoisuus vai sekin voi yhtä hyvin olla "$jeesusajaamopolla"!? Miksi sitten kuitenkin aina mainitaan erikseen kaikissa oppaissa parin muun muuttujan kanssa!? Selitä toi asia niin kuin selittäisit sen 4-vuotiaalle kummipojallesi ;)
PHP luo automaattisesti taulukot $_GET ja $_POST. Ne ovat siis aina samannimisiä ja sisältävät mahdolliset GET- ja POST-muuttujat. PHP-skriptin tekijä voi luoda myös omia taulukoita. Niiden nimen saa valita itse. Skriptissäsi $row on tällainen itse luotu taulukko. Siihen luetaan tietoja tietokannasta.
Osoitteen ja kenttien tarkistukset täytyy kirjoittaa ennen kuin tietoa tallennetaan tietokantaan, siis käytännössä skriptin alkupuolelle. Molemmat tarkistukset voi muotoilla niin, että skripti lopetetaan tarvittaessa niihin:
$osoite = "http://www.minunsivut.fi/vieraskirja.php"; if ($_SERVER["HTTP_REFERER"] <> $osoite) die("Virheellinen osoite!"); if ($_POST['viesti'] == "") die("Viesti puuttuu!");
Funktio die lopettaa skriptin virheilmoituksen saattelemana.
Lähetyssivun osoitteen tarkistus on kyllä minusta hieman turha, koska selaimen lähettämään HTTP_REFERER-tietoon ei ole luottamista.
Tähän väliin vähän offtopikkia:
Minkähän takia tuo tagi [/koodiphp] jää tavallaan päälle, ja värjää kaiken tekstin koodiruudukon jälkeen siniseksi?
Kiitos Antti. Pitää kokeilla noiden koodien lisäystä oikeeseen paikkaan.
Eikös se HTTP_REFERER juuri oikein tarkista sen, että mistä se tulee. Siis ainakin monessa paikassa tuota pidetään isona riskinä jos sitä ei tarkasteta. Muuten minun koodia voidaan käyttää väärin!?
(Mahdollisuus on kai yksi 100 miljoonasta, mutta kuitenkin =))
Referer tulee asiakkaalta, jolloin se on helppo väärentää.
Lisäksi se on speksin mukaan vapaaehtoinen otsake, eli asiakkaan ei ole sitä pakko lähettää, jolloin sen vaatiminen on tyhmää. Lisäksi useat tietoturvaohjelmat siivoavat tuon pois yksityisyyden nimissä--en minäkään pidä siitä, että kaikki epäilyttävät veppimestarit näkevät, missä oon surffaillu ja pidänkin oletuksena Operasta Refererin lähetystä pois päältä.
Antti80 kirjoitti:
Eikös se HTTP_REFERER juuri oikein tarkista sen, että mistä se tulee. Siis ainakin monessa paikassa tuota pidetään isona riskinä jos sitä ei tarkasteta.
Tuolla ei ole oikeastaan mitään tekemistä tietoturvan kanssa. Pointti on siinä, että tarkastat ne käyttäjän lähettämät tiedot ennen niiden laittamista sihen SQL-lauseeseen. Se on loppupeleissä aivan sama mistä se käyttäjä ne tiedot lähettää, aivan samanlailla niihin ei koskaan voi luottaa.
Eli perusjuttuja noista tarkistuksista.
Ja esimerkkejä seuraa:
<?php //tekstikenttien vastaanottamiseen, nyt siis käytössä MySQL-tietokanta. function get_user_text($data){ return get_magic_quotes_gpc() ? mysql_real_escape_string((stripslashes($data)) : mysql_real_escape_string($data); } // päivämäärien tarkistamiseen function check_date($date_time){ if(preg_match("/^([123456789][[:digit:]]{3})- (0[1-9]|1[012])-(0[1-9]|[12][[:digit:]]|3[01]) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $date_time, $part) && checkdate($part[2], $part[3], $part[1])){ return true; }else{ return false; } } //ja sitten käyttöesimerkki turvallisesta tietojen vastaanottamisesta //Kokonaisluku, jos hyväksytään myös liukuluvut, niin intval() pois if(isset($_POST['numero']) && is_numeric($_POST['numero'])){ $numero = intval($_POST['numero']); }else{ die('Numeroa ei lähetetty tai se ei ollut numeerinen'); } //tekstikenttä if(isset($_POST['teksti']) && trim($_POST['teksti']) != ""){ $teksti = get_user_text($_POST['teksti']); }else{ die('Tekstikenttää ei lähetetty tai se oli tyhjä'); } //päivämäärä if(isset($_POST['pvm']) && check_date($_POST['pvm'])){ $pvm = $_POST['pvm']; }else{ die('Päivämäärää ei lähetetty tai se oli virheellinen'); } // ja jos tänne asti päästään, niin voidaan turvallisesti laittaa syötteet SQL-lauseeseen ja ajaa se tietokantaan. ?>
Helppoa, vai mitä ;)
Hmm. Ok.
Täältä luin noita HTTP_REFERER ja muita asioita:
http://myy.helia.fi/~jaami/php/lomake.html
http://koti.mbnet.fi/stinger/web_sec2.php
Ajv, kiitos.
"Helppoa, sanoi mummon kun kahvimerkkiä vaihtoi" ;)
Pitää kuitenkin vähän sulatella viestiäsi.
Eli tekstikentän tarkistattessa se ei hyväksy mitään numeroita?
Eihän minun tässä (muutenkaan?) tarvitse tarkistaa päivämäärää, koska käyttäjä ei sitä syötä vaan se tulee automaattisesti palvelimelta!? Vai se on silti ilmeisesti syytä tarkistaa.
function get_user_text($data){ return get_magic_quotes_gpc() ? mysql_real_escape_string((stripslashes($data)) : mysql_real_escape_string($data); }
Tuota siis kannattaa käyttää vaikka olisi tuo "kenoviiva-asetus" automaattisesti päällä?
Tuon asetuksen jälkeen ei voi tekstikenttään laittaa esim. html-koodia? Oli miten oli niin miten voisi määritellä että voi käyttää esim. seuraavia tageja: <a href> <b> <i> <u>
Joo, tuo viesti ei nyt suoranaisesti ollut vastaus sun ongelmiin, vaan se oli yleinen saarna tosta tietoturvasta :) Eli ei sun tartte tarkastaa päivämäärää, jos se luodaan automaattisesti.
Tekstikenttään voi tietenkin laittaa numeroita, mutta numeeriseen kenttään ei voi laittaa tekstiä.
Tuota funktiota kannattaa käyttää, vaikka "kenoviiva-asetus" on päällä, sillä tuo funktio tarkistaa onko se päällä ja toimii sen mukaan. Tuo funktio ei kuitenkaan suodata html-tageja, sillä eihän html ole tietokannan kannalta mitenkään vaarallista koodia. Html:n suodattaminen tapahtuu joko strip_tags()-funktiolla tai sitten melkein parempi on käyttää bbcodea, joka sitten taas on tarina erikseen :)
http://myy.helia.fi/~jaami/php/lomake.html:
PHP määrittelee automaattisesti superglobaaleja ympäristömuuttujia taulukkoon. Usein on tärkeää tarkistaa että pyyntö palvelimelle lähetetään lomakkeeltasi, sillä joku on voinut tehdä omiin tarkoituksiinsa lomakkeen, joka lähettää haitallisissa tarkoituksissa pyyntöjä omaan sovellukseesi.
Sanoisin, että tuo on lähes täyttä potaskaa. Se krakkeri saa kyllä mun puolesta tehdä vaikka minkälaisen "haitallisen" lomakkeen, mutta jos perus-tietoturva on kunnossa, se ei saavuta sillä mitään muuta kuin harmaita hiuksia. Ja sitä paitsi jos joku haluaa oikeasti krakkeroida sivujasi, niin tuollaisen tarkistuksen osaa lapsikin ohittaa. Kuten jo aiemmin mainittu, tuo HTTP_REFERER on vapaaehtoinen tieto, jonka käyttäjä voi helposti väärentää.
Ah, PHP ja tietoturva, lempiaiheitani... :)
Heh,
http://myy.helia.fi/~jaami/php/
Heti toisella tunnilla aletaan opettamaan PHP:n säännöllisiä lausekkeita... Jotain pielessä? Tämä on juuri sitä kun kadulta otetaan bussikuskeja opettamaan AMK:hon ohjelmointia... :)
Nimim. AMK-ohjelmointikursseihin pettynyt.
Tai no, ehkä olisi parempi sanoa AMK-opetuksen tasoon pettynyt. En ehkä ole oikea henkilö arvostelemaan ohjelmoinnin opetusta, kun se on ollut itellä harrastuksena... Se siitä.
Kiitos ajv. Et sitten jaksaa omaa php-opasta kirjoittaa, kun sulla homma hyvin hallussa. ;)
Nojoo, kivempi jos täällä jaksat vastailla!
Joo, nuo koulun kurssit ovat aika suppeita tai sit käydään läpi koko PHP parissa tunnissa. Siitä ei paljoa opi. Kuitenkin innostuksen sain PHP:sta (+MySQL) ja täällä sitä yritetään lisää oppia.
Se vaan, että kun ei kirjoita tekstikenttiin niin se tappaa yhteyden niin se ei sitten käy läpi ainakaan minulla loppuja koodeja eli html-koodeja. Noh, ei se nyt kauhea haitta ole... Mutta jää kuitenkin tablesta pari solua tulostumatta (=sivujen pohjagrafiikkaa)
Pitää tässä jossain vaiheessa laittaa koodini esille niin voitte vielä tarkistaa.
Antti80 kirjoitti:
Se vaan, että kun ei kirjoita tekstikenttiin niin se tappaa yhteyden niin se ei sitten käy läpi ainakaan minulla loppuja koodeja eli html-koodeja. Noh, ei se nyt kauhea haitta ole... Mutta jää kuitenkin tablesta pari solua tulostumatta (=sivujen pohjagrafiikkaa)
Riippuu vähän miten pohjakraffat tulostat, mutta mulla ne on ala.php-nimisessä filussa jonka normaalisti include()lla nappaan skriptin lopuksi. Tässä sitten korvaisin pelkän dien niin, että ensin echolla kertoisin missä ongelma, sitten includaisin tuon ala.php:n, ja sitten kutsuisin die()tä ettei loppua skriptiä käytäisi. Mielestäni hyvä tapa.
Kiitos Sooda. Täytyy katsoa miten ratkaisen...
Mitä eroa näillä tarkistuksilla?
if(isset($_POST['teksti']) && trim($_POST['teksti']) != "" { $teksti = get_user_text($_POST['teksti']); }else{ die('Tekstikenttää ei lähetetty tai se oli tyhjä'); }
if ($_POST['viesti'] == "") echo "Viesti puuttuu!";
trim() poistaa whitespacen, eli esim. pelkkä välilyönti tulkitaan tuossa minun tavassa tyhjäksi viestiksi. Tuo isset() on sitten vähän kaksipiipunen juttu. Se on käytännössä turha, jos palvelimella on PHP:n asetuksissa määritelty huomautukset pois päältä (yleensä näin onkin), mutta itellä vain on tapana kirjotella sellaista koodia, joka toimii mahdollisimman globaalisti.
Eli pointti on siinä, jos tarkistat vain
if ($_POST['viesti'] == "") echo "Viesti puuttuu!";
ja huomautukset ovat päällä ja tuota kenttää ei ole lähetetty, niin PHP-tulkki antaa siitä huomautuksen, että muuttujaa ei ole määritelty.
Kerran muuten kirjotin jo puoliksi oppaan PHP:n tietoturvasta, mutta se hävisi jotenkin kummasti bittitaivaaseen, eikä ole sen jälkeen ollut aikaa kirjotella mitään ylimääräistä. Ja netissä on muutenkin kyllä hyviä php-oppaita ihan suomenkielelläkin. Esim Antin opas täällä putkassa.
Selkee.
Luinpa tuota Antin opasta uudestaan ja tarkemmin. Siellähän olikin hyviä vihjeitä lomake-koodaukseen. Tässä opettelun alkuvaiheessa vaan ärsyttää, että koko ajan tulee eri tavalla koodattuja lomakkeita (=eri muuttujat, eri tarkistukset, eri käskyt hakea kannasta tietoa jne jne), on liian paljon valinnan varaa. =)
Itse en ole muuten käyttänyt tätä tietokannan lopetusta:
mysql_close($yhteys);
Sain sen käsityksen ettei se ole tarpeellista, ainakaan minun "skripteissä"!?
Yhteys tietokantaan katkaistaan automaattisesti skriptin loputtua, joten välttämätöntähän se ei toki ole. Tietysti pidemmissä skripteissä yhteys on hyvä sulkea välittömästi kun sitä ei enää tarvita ja näin vapauttaa resursseja.
if(isset($_POST['nimi']) && trim($_POST['nimi']) != ""){ $nimi = get_user_text($_POST['nimi']); }else{ die('Nimikenttää ei lähetetty tai se oli tyhjä'); }
Tuo antaa oikein virheilmoituksen, jos nimi-kenttä tyhjä.
Mutta jos se täytetty niin se ei tallenna vaan tulee virhe:
Fatal error: Call to undefined function: get_user_text() in /sensuroitu/vieraskirja_tallennettu.php on line 38
Antti80 kirjoitti:
if(isset($_POST['nimi']) && trim($_POST['nimi']) != ""){ $nimi = get_user_text($_POST['nimi']); }else{ die('Nimikenttää ei lähetetty tai se oli tyhjä'); }Tuo antaa oikein virheilmoituksen, jos nimi-kenttä tyhjä.
Mutta jos se täytetty niin se ei tallenna vaan tulee virhe:Fatal error: Call to undefined function: get_user_text() in /sensuroitu/vieraskirja_tallennettu.php on line 38
Ei tallennakaan, koska se yrittää suorittaa get_user_text-funkiota, jota ei ole olemassa. ;)
EDIT: Mitäs tämä oikein lihavoi tekstiäni, vaikken ole sitä edes asettanut?
WTF!? :) Miksi ei ole olemassa, mitä nyt? :)
Antti80 kirjoitti:
WTF!? :) Miksi ei ole olemassa, mitä nyt? :)
No onko sinulla varmasti tämä koodi skriptissäsi?
<?php function get_user_text($data){ return get_magic_quotes_gpc() ? mysql_real_escape_string((stripslashes($data)) : mysql_real_escape_string($data); } ?>
Ups, pitääkö toi olla ennen vai jälkeen tuon nimikentän tarkistuksen jälkeen.
Oli miten oli niin tulee:
Parse error: parse error, unexpected ':' in /sensuroitu/vieraskirja_tallennettu.php on line 44
Antti80 kirjoitti:
Ups, pitääkö toi olla ennen vai jälkeen tuon nimikentän tarkistuksen jälkeen.
Oli miten oli niin tulee:
Parse error: parse error, unexpected ':' in /sensuroitu/vieraskirja_tallennettu.php on line 44
Tarkistusta ennemmin.
EDIT:
Funktiossa oli virhe. Toimiva koodi tässä:
<?php function get_user_text($data){ return get_magic_quotes_gpc() ? mysql_real_escape_string(stripslashes($data)) : mysql_real_escape_string($data); } ?>
EDIT: KIITOS, TOIMII!
-----------------------------
Tässä nyt koko tallentava koodi. Koitin kommentoida jokaista kohtaakin. Onko kommentoinnissa jotain väärin, entä tuleeko muuta huomautettavaa itse koodista?
<?php //Tietokantayhteyden luonti $dbh=mysql_connect ("SENSUROITU", "SENSUROITU", "SENSUROITU") or die ('Tietokantavirhe: ' . mysql_error()); mysql_select_db ("SENSUROITU"); /*tekstikenttien vastaanottamiseen, käytössä MySQL-tietokanta. Tarkistaa myös kenoviiva-asetuksen ja lisää ne jos ei automaattisesti tule palvelimelta.*/ function get_user_text($data) {return get_magic_quotes_gpc() ? mysql_real_escape_string(stripslashes($data)) : mysql_real_escape_string($data);} /*Tarkistetaanko onko kentät tyhjiä. trim() poistaa whitespacen, eli esim. pelkkä välilyönti tulkitaan tuossa tyhjäksi viestiksi. Tuo isset() on sitten vähän kaksipiipunen juttu. Se on käytännössä turha, jos palvelimella on PHP:n asetuksissa määritelty huomautukset pois päältä (yleensä näin onkin).*/ if(isset($_POST['nimi']) && trim($_POST['nimi']) != "") {$nimi = get_user_text($_POST['nimi']);} else {die('Nimikenttää ei lähetetty tai se oli tyhjä');} if(isset($_POST['viesti']) && trim($_POST['viesti']) != "") {$viesti = get_user_text($_POST['viesti']);} else {die('Viestikenttää ei lähetetty tai se oli tyhjä');} //Luodaan kaksi eri muuttujaa $sivunotsikko; $viestiteksti; //Tallennetaan "Vieraskirja" nimiseen taulukkoon (MySQL) nimi,viesti ja kirjoitusaika. $sql_lauseke = "insert into Vieraskirja (nimi,viesti,lisatty) values ('$nimi','$viesti',NOW()) "; /*Tietojen tallennus jos epäonnistuu niin tulostuu virheilmoitus. Jos tietojen tallennus onnistuu tulostetaan onnistuminen ja kaikki tallennetut tiedot.*/ if (!$haeviestit = mysql_query($sql_lauseke,$dbh)) { $sivunotsikko = "Viestin tallennus epäonnistui! "; $uutisteksti = "Virhe: " . mysql_error(); } else { $sivunotsikko = "Viestin tallennus onnistui!"; $viestiteksti = "Seuraavanlainen viesti talletettu:<p>"; $viestiteksti .= "Kirjoittaja: " . $nimi . "<br>"; $viestiteksti .= "Viesti: " . $viesti. "<br>"; } ?> <html> <head> <title><?php print "$sivunotsikko"; ?></title> </head> <body bgcolor="#CCCCCC"> <h2><?php print "$sivunotsikko"; ?></h2> <?php print "$viestiteksti"; ?>
lainaus:
/*tekstikenttien vastaanottamiseen, käytössä MySQL-tietokanta. Tarkistaa myös kenoviiva-asetuksen ja
lisää ne jos ei automaattisesti tule palvelimelta.*/
Itseasiassa tuo tarkistaa sen kenoviiva-asetuksen ja jos sen on päällä, niin se poistaa ne kenoviivat ja ajaa sen mysql_real_escape_string()-funktion läpi. Tuo "kenoviiva-asetus" (harvinaisen selkeä nimi muuten tuolle magiq_quotes_gpc:lle :-) ) kun ei aja ihan samaa asiaa kuin mysql_real_escape_string(). mysql_real_escape_string() ottaa huomioon tietokannan merkistön ja osaa escapettaa juuri oikeat merkit MySQL-tietokannan kannalta, kun tuo "kenoviiva-asetus" on vähän väljempi ja yleiskäyttöisempi.
Muuten tuo koodi näyttää kyllä tietoturvan kannalta hyvältä. Sisennys-tyylisi on hieman outo... :) Ja kun luot nuo muuttujat niin niille kantaa antaa myös tyhjä arvo:
$sivunotsikko = NULL;
Mutta kokonaisuudessaa hyvän näköistä koodia, tosta on hyvä jatkaa! Varsinkin noi muuttujat olet onnistunut nimeämään hyvin. Itellä ne tuppaa olemaan sellasia yksi- tai kaksikirjaimisia lyhenteitä mitä epämääräisimmistä asioista ja yleensä vielä suomen, ruotsin ja englannin kielellä ja ilman kommentointia - tottakai!
Kiitos, muokkasin sen kommentoinnin.
$sivunotsikko=NULL;
$viestiteksti=NULL;
Noinko? Mitä hyötyä käytännössä tosta on?
Itse olen maailman huonoin kommentoimaan ja tekemään selkeää koodia, mutta nyt on oikeasti pakko jos haluan jotain oppia... Ja en ole näytä samoja asioita kysymyssä ensi viikolla seuraavaan skriptiini.
No ei kai tuostakaan mitään suoranaista hyötyä tuossa koodissa ole, mutta se on ihan hyvä tapa opetella alustamaan ne muuttujat - monissa muissa kielissä se on melkein pakollista. PHP:ssä se vain estää tuon Laaksosen Antinkin jo tässä threadissa esittämän tietoturva-riskin mahdollistumisen.
Joo, ja on muuten ihan ilo auttaa, kun joku tosissaan haluaa oppia. Tsemppiä vaan oppimiseen :)
Ai niin ja olisi ehkä myös mainita, miksi moiset tarkistelut ovat tarpeen. Kannattaa lukaista myös tämä artikkeli läpi.
Ok. Pitääpä lukea toi artikkeli ajatuksella.
Jes, itse olen aika huono (ei kärsivällisyyttä?) lukemaan oppaita, mutta erilaisia skriptejä tekemällä ja muilta oppimalla opin paremmin.
Ajattelin vielä evästeen laittaa tuohon, että 5 min sisään ei voi uudestaan lähettää. (Joojoo, evästeet helppo kiertää, mutta ei kaikki sitä tajua)
Täältä katoin mallia:
https://www.ohjelmointiputka.net/keskustelu/1468-php-keksi-tallentumaan-lomakkeessa
Mutta virhettä pukkaa minulla. Se ei tottele evästä ja tallentaa kaikki tiedot joka kerta kantaan, virhe kuitenkin tulee:
Warning: Cannot modify header information - headers already sent by (output started at /sensuroitu/vieraskirja_tallennettu.php:7) in /sensuroitu/vieraskirja_tallennettu.php on line 109
if ($_COOKIE['aikarajoitus5min'] == "kylla") { header("Location: vieraskirja_aikarajoitus5min.html"); } // Tästä sitten alkaa koodi joka suoritetaan // jos lomaketta ei oltu lähetetty vielä /*Luo evästeen aikarajoitus, jonka arvo on kylla. Tämä eväste on voimassa seuraavat 5 minuuttia*/ setcookie("aikarajoitus5min", "kylla", time() + 60 * 5);
Tulostat jo jotain rivillä 7: http://pp.kpnet.fi/blaze/codefaq/#headerssent
Heh, hyvä sivu sulla. :)
Sori, nyt ei kyllä mene minun tajuntaani toi asia.
"älä tulosta mitään ennen niitä otsakkeita"
Eli mitä? Eihän voi olla tulostamatta mitään?
Tämäkin pakko laittaa tulostusten jälkeen!?
setcookie("aikarajoitus5min", "kylla", time() + 60 * 5);
Tuota kysytään tosi usein, oli pakko tehä jotain :)
Periaatehan on siis se, että tämän sijasta
teet näin
Edit: ohops, siinä oli vielä toinenkin kysymys:
Antti80 kirjoitti:
Tämäkin pakko laittaa tulostusten jälkeen!?
setcookie("aikarajoitus5min", "kylla", time() + 60 * 5);
Eikun nimenomaan ennen tulostuksia. Ja joo, kaikki, mikä koskee header()-funktiota, koskee setcookietakin. Molemmat lähettävät HTTP-headerin.
<?php if ($_COOKIE['aikarajoitus5min'] == "kylla") {header("Location: vieraskirja_aikarajoitus5min.html");}?> <?php setcookie("aikarajoitus5min", "kylla", time() + 60 * 5); ?>
Noi laitoin nyt ihan ylös ja hyvin toimii nyt. :)
Oletin vain, että toi "setcookie" olisi pitänyt laittaa vasta skriptin loppuun, muuten se eväste olisi heti päällä kaikille. Mut hyvä näin!
<?php if ($_COOKIE['aikarajoitus3min'] == "kylla") {header("Location: vieraskirja_aikarajoitus3min.html");}?> <?php setcookie("aikarajoitus3min", "kylla", time() + 60 * 3); ?>
Mielestäni sain tuon toimimaan, mutta nyt se kyllä sanoo, että virhe tulee jos yrittää 3 min sisään kirjoitella, mutta se kuitenkin tallentaa kaikki kirjoitukset kantaan. Mitäköhän nyt menin muuttamaan väärin...
Kokeile lisätä header-jutun perään exit; en ole nyt varma mutta mielestäni header-function jälkee koodi suoritetaan loppuun normaalisti..:/
Kiitos Mazuli, näytti auttavan ongelmaan.
Eräältä tuttavaltani (guru ohjelmoinnissa) kyselin vielä vinkkejä, tässä pari seikkaa.
* mysql_error() funktion käyttö
Käytä tuota vain kehitysympäristössä. Julkisille webbisivuille ei kannata printata ulos mysql_errorin tietoja, sillä niistä saa irti arvokasta tietoa... Virheet voit tallentaa vaikka palvelimen lokiin tms.
tekstitiedostoon.
Mielestäni aika ylivarovaisuutta, mutta voihan tuokin olla uhka.
* die:n käyttö
mietippäs mitä tapahtuu mm. sivun esitykselle jos pätkäiset skriptin suorituksen kesken kaiken die rakenteella.
Tämän jo tiesinkin. Siinäpä olisi php:n kehittäjille ideaa, että kehittäisivät die-käskyn joka tappaisi vain php-koodin käsittelyn eikä koko sivua! =)
* magic_quotes
Parempiakin tapoja on. Jos käytät, niin
lue https://www.php.net/magic_quotes
Tämä menee kyl vähän yli mun ymmärryksen. Tiedän mitä se tekee, mutta liian monimutkainen =)
*Miksi kaikki syötteet on käsiteltävä tarkasti jokaista tilannetta varten:
http://www.colder.ch/news/08-31-2005/7/demonstration-of-php-s-ma.html
*isset
EI tarkista ovatko muuttujat tyhjiä - se tarkistaa onko määrätty muuttuja määritelty.
Aivan, tuon muokkasin kommentointiini.
* muuttujien käsittely...
tee lisää tarkistuksia... mitä tapahtuu tallennuksessa jos syötän nimikenttään 10,000 merkkiä pitkän arvon?
Mites tämän toteuttaisi? Eikö tämä riittänytkään:
<input type=text name="nimi" size="15" maglength="30">
*Suorituskykyyn ym. hallintaan liittyen. Kapseloi noita
koodinpätkiäsi funktioiksi. Siten ei tarvitse tehdä samoja rivejä useampaan paikkaan.
jaahas, pitää näiden käytöstä oppaista ilmeisesti lukea.
Kommentoikaa vapaasti edellä mainittuihin asioihin ja neuvokaa. :)
Kyllä nuo kaikki ihan vankkaa asiaa ovat. Tosin ei ensimmäiseltä ohjelmalta voi millään kaikkea tuollaista vaatia... :) Tuo pelkkä die() on tosiaan huono ratkaisu. Mutta tässä threadissa keskityttiin tuohon tietoturvaan eikä siihen kuinka rakentaa järkevä virheenkäsittely sivuille. Se onkin sitten asia erikseen... :)
Jees, mutta kun tätä koodia käytän pohjana varmistikin monille muille skripteille niin olisi hyvä tämä tehdä hyvin ja kiva olisi ymmärtää lähes kaikki. :)
TOISTAN:
"Siinäpä olisi php:n kehittäjille ideaa, että kehittäisivät die-käskyn joka tappaisi vain php-koodin käsittelyn eikä koko sivua!" EIKÖ, vai onko jotain die:n kieroteitä? =)
"Mitä tapahtuu tallennuksessa jos syötän nimikenttään 10,000 merkkiä pitkän arvon?"
Miten tämä estettäisiin. Nimi-kenttään max. 30 merkkiä ja viestikenttään max. 2000 merkkiä.
Tekstin pituuden tarkistaminen tapahtuu strlen()-funktiolla. Ihan käypä tarkistus, vaikka ei suoranaisesti tietoturvaan vaikuta (oli myös mulla mainittuna tuolla ;-)). Mutta ei siinä mitenkään käy, vaikka sinne kantaan tunkisi 1 000 000 merkkiä, kyllä se tietokanta sitä dataa syö. Saattaa vaan hieman kestää ja jossain vaiheessa tulee tietokannan/PHP:n omat limitit vastaan.
Toi virheenkäsittely onkin sitten hieman hankalampi homma. Itellä on virheille kokonaan oma luokka. Se hanskaa virheet ja logittaa tiedostoon ja huolehtii monesta muustakin asiasta, mutta toteutus on kaikkea muuta kuin esimerkillinen... :) Varmaan manuaalista löytyisi paljon parempia esimerkkejä...
Tässä nyt ihan ajatustasolla pseudo-koodia, miten tehdä simppeli virheenkäsittely:
if (strlen($nimi)>30)
{
exit ("Virhe! Nimessäsi ei saa olla yli 30 merkkiä!");
}
if (strlen($viesti)>4000)
{
exit ("Virhe! Viestissäsi ei saa olla yli 4000 merkkiä!");
}
Noin tein...
EDIT: Ahaa, lisäsit tuon esimerkkisi. Tutkitaan...
EDIT2: Mikä ero "exit" ja "die"?
Antti80 kirjoitti:
Mikä ero "exit" ja "die"?
Lainaus php.net:stä (jota kannattaa muuten lukea):
"The die() function is an alias for exit()."
function get_user_text($data)
{return get_magic_quotes_gpc() ? mysql_real_escape_string(stripslashes($data)) : mysql_real_escape_string($data);}
Tuossa on tuo muuttuja "data". Onko se joku yleinen käsite vai pitäiskö toi funktio hässäkkä olla
niin monta kertaa kuin monta muuttuja minulla käytössä!? *pihalla*
Tuo muuttuja $data on tuon funktion sisäinen muuttuja, se ei ole käytössä kuin funktion sisällä eikä siihen voi viitata muualla koodissa eikä siitä näin ollen tarvitse sen enempää völittääkkään :)
Ok, eli se hoitaa yksinään kaiken datan (=tekstikenttien) tarkistuksen. Hyvä!
Ajv: Kattelin tuota sinun tarkistus esimerkkiä. Vähän ihmetyttää, että miksi se tulostaisi header.php ja footer.php kun aikaisemmin skripti (virheen takia) voi "kuolla".
"tulostaa headerin ja footerin ja niiden välissä käsittelee virheet". Tosin en tuotakaan ihan tajunnut... Miten niin niiden välissä? Vai tohon koodiin pitää ite vielä kirjoittaa tarkistus-koodia!? Oli miten oli niin eikö kaikki muu sisältö pitäisi saada tulostetuksi ennen kuin se herjaa virheestä tai "tappaa" koko skriptin.
Siis header- ja footer ovat yleensä ne sivun raamit, joiden väliin sisältö laitetaan. Esim:
***header.php <html> <head><title>Esimerkki</title></head> <body> <?php //tänne esim. tietokantayhteyksien luomiset yms. ?> ***footer.php <?php //tänne tietokantayhteyden sulkeminen ym. asiat, jotka tehdään sivun lopussa ?> </body> </html> ***esimerkkisivu.php <?php include('header.php'); ?> <h1>Tämä tässä en esimerkkisivu</h1> <?php include('footer.php'); ?>
Aivan, ollaan samalla aaltopituudella. :)
Mutta tuossa tapauksessa skripti pitää pilkkoa ja tappaa vasta sivun viimeisenä...
Esim:
if(isset($_POST['nimi']) && trim($_POST['nimi']) != "") {$nimi = get_user_text($_POST['nimi']);} else {die('Nimikenttää ei lähetetty tai se oli tyhjä');}
Tuo koodi pitäisi olla viimeisenä muiden mahdollisten "tappajien" kanssa? Mutta sittenhän se ehtii jo tallettaa kantaan tietoja... Äh, ymmärsitköhän mitä hain takaa.
Eli:
Nyt siis tämä hyväksyy kaikki html-koodit!?
Miten saa html-koodit pois käytöstä, tai paras olisi että voisi käyttää vain muutamaa html-tagia. Esim. <a href> <b> <i> <u>
https://www.php.net/strip_tags
https://www.php.net/htmlspecialchars
https://www.php.net/htmlentities
Kiitos Sooda. Ei kyllä osaaminen/kärsivällisyys riitä ihan noiden kaikkien ymmärtämiseen, ainakaan heti.
Lisäsin näin:
//poistettaan HTML-tagit ja syötteeseen sekoitetut SQL-kyselylauseet näin: $viesti = strip_tags ($_POST['viesti']);
Haluaisin kyllä sallia <b><a><i><u> html-tagit. En kyllä ymmärtänyt että mihin/miten lisään tuon sallimisen vaikka noiden Soodan antamien linkkien takaa löytyy monta esimerkkiä.
Kun tein noin siltikin tämä (kenoviiva-asetus) pitää lisätä koodiin, eikö?:
function get_user_text($data) {return get_magic_quotes_gpc() ? mysql_real_escape_string(stripslashes($data)) : mysql_real_escape_string($data);}
Tää koko kenoviiva-asetus hässäkkä on kyllä niin kauheeta ymmärtää, kun on miljoona tapaa tehdä tämä. =) Ja ainakin minun palvelintarjoajalla on kenoviiva-asetus päällä niin ei tästä tarvitsi välittää, mutta olisi hyvä kuitenkin oppia.
Antti80 kirjoitti:
Ja ainakin minun palvelintarjoajalla on kenoviiva-asetus päällä niin ei tästä tarvitsi välittää, mutta olisi hyvä kuitenkin oppia.
"kenoviiva-asetus" ei aja samaa asiaa kuin mysql_real_escape_string(). Kuten aikaisemmin sanoin mysql_real_escape_string() ottaa huomioon tietrokannan käyttämän merkistön ja osaa sen perusteella "escapettaa" kaikki tietokantakohtaiset kriittiset merkit.
Jos haluat sallia tiettyjä html-koodeja (<b><a><i><u>), niin se onnistuu antamalla strip_tags()-funktiolle ne sallitut tagit parametrinä. Mutta entäpä jos haluat viitata artikkelissasi esim. <b>-tagiin, niin mitenkäs meinasit sen toteuttaa. Tai kirjoitat artikkelin <div>-tagista, mutta koodisi strippaakin kaikki '<div>'-merkkijonot pois artikkelistasi.
No toki voit kirjoittaa artikkelin käyttämällä html-entiteettejä, mutta se paras tapa lienee ajaa kaikki data htmlentities()-fukntion läpi ja käyttää muotoiluun bbcodea.
Aihe on jo aika vanha, joten et voi enää vastata siihen.