HTML/PHP - tillåt endast siffror och : (kolon) i fält

Permalänk
Medlem

HTML/PHP - tillåt endast siffror och : (kolon) i fält

Skulle googla på det här, men vet inte riktigt på vad. Hittade en del, men inget som hjälpte mig direkt, så nu ger jag upp och frågar er ist:

I ett fält jag har, så vill att man bara ska kunna ange tid, i formatet HH:MM:SS. Nu så kan man dock skriva in vad som helst, å de blir ju lite fel då.
Så hur kan jag göra så att endast siffror och : (kolon) (i HH:MM:SS format) är tillåtna, och om någon gör fel så varnar den om det (javascript popup eller nåt...)

Tack.

Permalänk
Medlem
Permalänk
Inaktiv

<input type="text" required pattern="([0-9]{2}:[0-9]{2}:[0-9]{2})">

user discretion is advised

Permalänk
Permalänk
Medlem
Skrivet av anon146897:

<input type="text" required pattern="([0-9]{2}:[0-9]{2}:[0-9]{2})">

user discretion is advised

Tack det där var exakt vad jag sökte efer. Det där sist nämnda förstår jag dock inte

Permalänk
Hedersmedlem

När det gäller tidsstämplar och datumkontroller så är det många som försöker uppfinna hjul om och om igen, när det redan finns väldigt många inbyggda funktioner som tar hänsyn till väldigt många saker. Att tolka datum manuellt är aldrig bra.

Om du vill kontrollera att det är en giltig tidssträng så kan du i PHP i detta fall med fördel använda strptime(). Du ger funktionen två argument: tidssträngen och dess tänkta tolkning, och om tidssträngen inte är en giltig tidsstämpel enligt mönstret så returneras false. Exempelvis strptime("10:61:14", "%H:%M:%S") → false, men strptime("10:59:14", "%H:%M:%S") → [array med datumet, vilket tolkas som true i PHP].

Så i praktiken:

if (strptime($tidsstampel, '%H:%M:%S')) { //Datum OK } else { //Datum inte OK }

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Inaktiv
Skrivet av lambdaN:

Tack det där var exakt vad jag sökte efer. Det där sist nämnda förstår jag dock inte

Antar att han syftar på att stödet inte finns i alla webbläsare. Se https://developer.mozilla.org/en-US/docs/HTML/Element/Input#B... och gå ner till pattern.

Sen måste du kolla samma sak på servern också, då det enkelt går att modifera HTML koden i webbläsaren.

Permalänk
Skrivet av anon146897:

<input type="text" required pattern="([0-9]{2}:[0-9]{2}:[0-9]{2})">

user discretion is advised

99:99:99 är ju ett intressant klockslag

Permalänk
Hedersmedlem
Skrivet av Yxskaftet:

99:99:99 är ju ett intressant klockslag

Precis, det är alltid klurigt att parsea tid själv .

I HTML5:

<input type="text" required pattern="([0-1][0-9]|2[0-3])(:[0-5][0-9]){2}">

Detta tillåter bara giltiga "HH:MM:SS"-format. Kombinera detta med strptime() i PHP som jag nämnde ovan så ska det vara klart.

Och se till att dokumentet tolkas som HTML5, så klart.

Notera återigen det som nämndes tidigare att vad gäller klientsidekontroller som denna så finns det inget som säger att klienten faktiskt måste lyda. Exempelvis en browser som inte implementerat denna funktion i HTML5 kommer ju bara ignorera HTML5-specifikationen, så PHP-kontrollen på serversidan måste göras. Klientkontroller är bara ett hjälpmedel för just klienten, egentligen.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Inaktiv
Skrivet av Yxskaftet:

99:99:99 är ju ett intressant klockslag

Annars var det en liten detalj som OP kunde fixat och samtidigt lärt sig lite regex.

Permalänk
Medlem
Skrivet av phz:

Precis, det är alltid klurigt att parsea tid själv .

I HTML5:

<input type="text" required pattern="(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}">

Detta tillåter bara giltiga "HH:MM:SS"-format. Kombinera detta med strptime() i PHP som jag nämnde ovan så ska det vara klart.

Och se till att dokumentet tolkas som HTML5, så klart.

Notera återigen det som nämndes tidigare att vad gäller klientsidekontroller som denna så finns det inget som säger att klienten faktiskt måste lyda. Exempelvis en browser som inte implementerat denna funktion i HTML5 kommer ju bara ignorera HTML5-specifikationen, så PHP-kontrollen på serversidan måste göras. Klientkontroller är bara ett hjälpmedel för just klienten, egentligen.

Hm, ok har lagt in strptime och den där required pattern där. Verkar ju funka.

Om ni vill se vad jag gör, så kolla på http://lambdan.com/r/ -> Add run

Nu till min nästa fråga: som ni ser har jag "Date done" på Add run, och jag kan ju enkelt få pattern jag vill med require pattern igen. Men då måste ju folk fylla i den, vilket de inte måste. Fyller de inte i den så ska det bli dagens datum. Sen vore det bra om även X gick att lägga in, i fall man inte vet exakt datum t.ex.

Idéer?

PHP koden har jag gjort så här med iaf, men det är sedan att använda "required pattern" om de fyller i något som jag måste fixa:

if(!$_POST['Date']) { $DateTime=date("Y-m-d",time()); }

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Nu till min nästa fråga: som ni ser har jag "Date done" på Add run, och jag kan ju enkelt få pattern jag vill med require pattern igen. Men då måste ju folk fylla i den, vilket de inte måste. Fyller de inte i den så ska det bli dagens datum. Sen vore det bra om även X gick att lägga in, i fall man inte vet exakt datum t.ex.

Notera att "required" och "pattern" är separata attribut. Det är alltså inte "required pattern", så att säga. Ett enstaka attribut i HTML innehåller aldrig ett mellanslag.

I XHTML så hade man skrivit

required="required" pattern="..."

men man behöver inte sätta liknande värden på attribut som inte behöver några sådana i korrekt HTML.

Så ditt problem löses helt enkelt genom att inte skriva "required" men ha kvar "pattern". Då kan användare läman fältet blankt, men om de skriver något så måste det stämma med "pattern".

(Notera att jag redigerade mitt mönster lite efter att jag först postat det; ser att du citerat när det gamla fortfarande stod kvar. Båda fungerar, men det andra är lite kortare. Kanske kan vara belysande att titta på skillnaderna för att se vad mönsterna betyder, och då kanske även enkelt se hur man löser din andra fråga: hur man tillåter värdet "X" också.)

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av phz:

Notera att "required" och "pattern" är separata attribut. Det är alltså inte "required pattern", så att säga. Ett enstaka attribut i HTML innehåller aldrig ett mellanslag.

I XHTML så hade man skrivit

required="required" pattern="..."

men man behöver inte sätta liknande värden på attribut som inte behöver några sådana i korrekt HTML.

Så ditt problem löses helt enkelt genom att inte skriva "required" men ha kvar "pattern". Då kan användare läman fältet blankt, men om de skriver något så måste det stämma med "pattern".

(Notera att jag redigerade mitt mönster lite efter att jag först postat det; ser att du citerat när det gamla fortfarande stod kvar. Båda fungerar, men det andra är lite kortare. Kanske kan vara belysande att titta på skillnaderna för att se vad mönsterna betyder, och då kanske även enkelt se hur man löser din andra fråga: hur man tillåter värdet "X" också.)

Vet inte om det blev som du ville, men så här gjorde jag. Funkar perfekt!

<input type="text" name="Date" pattern="([0-9,X]{4}-[0-9,X]{2}-[0-9,X]{2})">

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Vet inte om det blev som du ville, men så här gjorde jag. Funkar perfekt!

<input type="text" name="Date" pattern="([0-9,X]{4}-[0-9,X]{2}-[0-9,X]{2})">

"," ska du inte ha med, för nu är det ett giltigt tecken att skriva in. Så som du har skrivit så är även t ex "7,XX-X8-00" giltig input, men inte ett speciellt bra datum.

Ekvivalent med ditt exempel men enklare:

<input type="text" name="Date" pattern="(XXXX-XX-XX|([0-9]{4}(-[0-9]{2}){2}))">

Med lite hänsyn tagen till att vi bara vill ha giltiga datum:

<input type="text" name="Date" pattern="(XXXX-XX-XX|([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])))">

Ingen månad har 0 eller >32 dagar, inget månadsnummer är 0 eller >12. Vi går inte "all-in" och kontrollerar att man inte anger t ex 31 april, men skulle man gå så långt så skulle man även kunna titta på skottår och mängder med specialregler, så detta är en rimlig begränsning. Likaså att vi håller oss e Kr och innan år 10000.

Men det låter jobbigt att de ska behöva skriva "XXXX-XX-XX" för att ange att de inte har något datum, men ändå inte vill att det ska anges som dagens datum. Lättare är att låta de skriva bara "-" för detta ändamål. Exempel:

<input type="text" name="Date" pattern="(-|([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])))">

Sköt hanteringen av strängen "-" specifikt i PHP-koden.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av phz:

bra grejjor

Ugh, tror inte jag fattar hur du menar. Testade med alla de där pattern'a, men ingen funkar då med t.ex. 2012-01-XX.
Gjort så här nu

<input type="text" name="Date" pattern="(-|[0-9X]{4}-[0-9X]{2}-[0-9X]{2})">

Å det verkar funka.
Testa lägga in X (på ställen där jag trodde passa) i din sistnämnda pattern, men då funkade inget

Dock vet jag inte vad jag ska koda med - i PHP som. Jag menar, - är väl en bra symbol för inget datum ?

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Ugh, tror inte jag fattar hur du menar. Testade med alla de där pattern'a, men ingen funkar då med t.ex. 2012-01-XX.
Gjort så här nu

<input type="text" name="Date" pattern="(-|[0-9X]{4}-[0-9X]{2}-[0-9X]{2})">

Å det verkar funka.
Testa lägga in X (på ställen där jag trodde passa) i din sistnämnda pattern, men då funkade inget

Dock vet jag inte vad jag ska koda med - i PHP som. Jag menar, - är väl en bra symbol för inget datum ?

OK, jag trodde du antingen ville att de skulle kunna skriva ett datum, men om de inte visste så skulle de bara skriva "-". Men du vill att de ska kunna ange t ex "2012-01-XX" ifall de vet att det var i januari, men inte vet vilken dag?

Som du skriver ovan skulle ju t ex mönstret "X01X-X8-3X" passa, och även "2013-99-00", och det är ju lite skumt. Skulle det vara OK att de antingen får ange "(exakt år eller inget alls)-(exakt månad eller ingen alls)-(exakt dag eller ingen alls)"?

Isf skulle jag kanske mer rekommendera

<input type="text" name="Date" pattern="([0-9]{4}|XXXX)-(0[1-9]|1[0-2]|XX)-(0[1-9]|[12][0-9]|3[01]|XX)">

Nu skulle de kunna ange t ex "XXXX-XX-07" osv vilket också är konstigt.

Kanske du framför allt skulle göra en select-meny med år, månader och dagar och kanske javascriptkontroll på vad som är rimligt. "pattern"-attributet lämpar sig bäst när man faktiskt har ett bestämt mönster som input ska vara, men när komplexiteten drar iväg så är det fel verktyg för uppgiften. Fundera också på om du inte lika gärna skulle kunna ha enkla select-menyer för år, månad, dag med färdigifyllda rimliga värden.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av phz:

Hmm gillar inte menyer där man väljer datum, personligen, då det tar längre att ange.

Dock är det sant som du säger att XXXX-XX-07 är konstigt, så gjorde pattern så här (tog bara bort XXXX från din.)

<input type="text" name="Date" pattern="([0-9]{4})-(0[1-9]|1[0-2]|XX)-(0[1-9]|[12][0-9]|3[01]|XX)">

Dock undrar jag om t.ex. 2012-01-2X funkar nu? Ser inte ut som det när jag kollar på det, och när jag testar. Tror du det här funkar, eller kan man exploita det också:

<input type="text" name="Date" pattern="([0-9]{4})-(0[1-9X]|1[0-2X]|XX)-(0[1-9X]|[12][0-9X]|3[01X]|XX)">

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Hmm gillar inte menyer där man väljer datum, personligen, då det tar längre att ange.

Dock är det sant som du säger att XXXX-XX-07 är konstigt, så gjorde pattern så här (tog bara bort XXXX från din.)

<input type="text" name="Date" pattern="([0-9]{4})-(0[1-9]|1[0-2]|XX)-(0[1-9]|[12][0-9]|3[01]|XX)">

Dock undrar jag om t.ex. 2012-01-2X funkar nu? Ser inte ut som det när jag kollar på det, och när jag testar. Tror du det här funkar, eller kan man exploita det också:

<input type="text" name="Date" pattern="([0-9]{4})-(0[1-9X]|1[0-2X]|XX)-(0[1-9X]|[12][0-9X]|3[01X]|XX)">

Det finns inget att egentligen "exploita", eftersom den "riktiga" kontrollen ska ske genom PHP, eftersom det är här som datumangivelsen faktiskt måste behandlas. Kontroller på klientsidan är egentligen bara en smidighetsfaktor för användaren, så att den inte ska behöva skicka iväg en förfrågan som ändå inte kommer godkännas av servern.

Ditt sista mönster tillåter inte att man anger "wildcards" för årtalet, t ex "201X-XX-XX", eller ens "XXXX-XX-XX" vilket väl var första anledningen till att inte bara använda ett vanligt datummönster, men däremot tillåter den märkligheter som "2012-XX-31"

Om man vill göra som du vill här så behöver man ett mönster som behandlar varje "wildcard" för sig, dvs antingen är alla tecken "X", eller så är alla tecken utom det första "X", eller så är alla tecken utom de två första "X", eller… Det går snabbt att skriva från tidigare regler med lite copy-paste:

<input type="text" name="Date" pattern="(XXXX-XX-XX|[0-9]XXX-XX-XX|[0-9]{2}XX-XX-XX|[0-9]{3}X-XX-XX|[0-9]{4}-XX-XX|[0-9]{4}-[01]X-XX|[0-9]{4}-(0[1-9]|1[12])-XX|[0-9]{4}-(0[1-9]|1[12])-[0-3]X|[0-9]{4}-(0[1-9]|1[12])-(0[1-9]|[12][0-9]|3[01]))">

Det är väldigt rättframt att skriva ett sådant mönster om man förstår paranteserna och |-tecknets roll. Ovanstående ska inte vara några problem att läsa, utöver att det är långt.

Vill du som i ditt sista exempel inte tillåta wildcards i årsangivelsen så kortas det ner till t ex:

<input type="text" name="Date" pattern="([0-9]{4}-XX-XX|[0-9]{4}-[01]X-XX|[0-9]{4}-(0[1-9]|1[12])-XX|[0-9]{4}-(0[0-9]|1[12])-[0-3]X|[0-9]{4}-(0[0-9]|1[12])-(0[1-9]|[12][0-9]|3[01]))">

Eftersom du inte kommer använda ett standarddatumformat, utan tillåta wildcards, så kommer PHP-kontrollen också bli klart mer invecklad istf att bara kolla om det är ett giltigt datum.

I praktiken kanske det är skumt att öht tillåta partiella wildcards i t ex månads/datumsangivelse av typen "2012-1X-XX" och "2012-12-2X". Mer konsekvent vore att antingen ange korrekt månad eller ingen alls, och korrekt dag eller ingen alls. Då kan ett mönster vara:

<input type="text" name="Date" pattern="[0-9]{4}-(XX-XX|((0[1-9]|1[12])-(XX|(0[1-9]|[12][0-9]|3[01]))))">

Detta tillåter datum på formen "2013-02-25", "2013-02-XX", "2013-XX-XX" men inga övriga varianter (utöver helt blankt).

Återigen så är select-formulär troligen enklare. Att det tar längre tid att fylla i är en sanning med modifikation: det tar lika många knapptryckningar att markera första rullistan och trycka "2013<Tab>02<Tab>25" (eftersom alla (?) browsers söker automatiskt i markerad rullist om man trycker på tangenter) som att markera textfältet och trycka "2013-02-25". Det tar färre knapptryckningar att skriva "2013<Tab><Tab>" än "2013-XX-XX".

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

Hmm. tror jag kör med den där sist nämnda pattern där. Men kan man modifiera den så här?

<input type="text" name="Date" pattern="[0-9]{4}-(XX-XX|[1-9]X|X[1-9]((0[1-9]|1[12])-(XX|[1-9]X|X[1-9]|(0[1-9]|[12][0-9]|3[01]))))">

+ så att 4X inte går osv.

Då borde ju iaf 2012-1X-3X eller 2012-X1-X3 t.e.x funka? Kan se skumt ut, men vill att det ska gå

Permalänk
Medlem

http://tools.netshiftmedia.com/regexlibrary/#

Där kan du kolla om din regex funkar i realtid - Helt oumbärligt för oss icke-savanter som inte kan läsa regexp som en bok.

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Hmm. tror jag kör med den där sist nämnda pattern där. Men kan man modifiera den så här?

<input type="text" name="Date" pattern="[0-9]{4}-(XX-XX|[1-9]X|X[1-9]((0[1-9]|1[12])-(XX|[1-9]X|X[1-9]|(0[1-9]|[12][0-9]|3[01]))))">

+ så att 4X inte går osv.

Då borde ju iaf 2012-1X-3X eller 2012-X1-X3 t.e.x funka? Kan se skumt ut, men vill att det ska gå

Varför vill du att det ska funka? Vill du täcka in fallen när användare vill ange:

  • –Jag vet att jag gjorde det på en månad som slutar på 1, men minns inte om det var januari eller november.

  • –Jag vet att jag gjorde det på en dag som slutade på 3, men inte om det var den tredje, trettonde eller tjugotredje.

  • –Jag vet att jag gjorde det på en månad som börjar på 1 och en dag som börjar på 3, men inte om det var 10–31, 11–30, 12–30 eller 12–31.

Det är ju nonsensinput . Hur ska du kunna sortera på datum? Låter som en ogenomtänkt designidé som du kommer märka är dålig när du ska börja försöka hantera data på serversidan.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av phz:

Varför vill du att det ska funka? Vill du täcka in fallen när användare vill ange:

  • –Jag vet att jag gjorde det på en månad som slutar på 1, men minns inte om det var januari eller november.

  • –Jag vet att jag gjorde det på en dag som slutade på 3, men inte om det var den tredje, trettonde eller tjugotredje.

  • –Jag vet att jag gjorde det på en månad som börjar på 1 och en dag som börjar på 3, men inte om det var 10–31, 11–30, 12–30 eller 12–31.

Det är ju nonsensinput . Hur ska du kunna sortera på datum? Låter som en ogenomtänkt designidé som du kommer märka är dålig när du ska börja försöka hantera data på serversidan.

Hmm, sant. Okej. Tror jag nöjer med det jag har. Nu måste jag väl dock fixa någon slags PHP kod för datumet? Har ju strptime(%H, %M, %S) eller nåt för tid, men hur blir det för det här? Gissar på strptime(%Y-%M-%D) eller nåt (lat)

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Hmm, sant. Okej. Tror jag nöjer med det jag har. Nu måste jag väl dock fixa någon slags PHP kod för datumet? Har ju strptime(%H, %M, %S) eller nåt för tid, men hur blir det för det här? Gissar på strptime(%Y-%M-%D) eller nåt (lat)

Ja, det är nu du får skörda att ditt datumformat inte är speciellt konsekvent . "XX" är ju ingen korrekt varken månads- eller dagsangivelse enligt något vettigt datumsystem. Du får göra en egen variant som verifierar formen på indata iom att du tillåter inkompletta datumangivelser.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av phz:

Ja, det är nu du får skörda att ditt datumformat inte är speciellt konsekvent . "XX" är ju ingen korrekt varken månads- eller dagsangivelse enligt något vettigt datumsystem. Du får göra en egen variant som verifierar formen på indata iom att du tillåter inkompletta datumangivelser.

Kom på en till sak, som jag inte vet hur jag ska fixa. Om någon skriver in ett ' i nåt av fälten, speciellt Runner och Game, så läggs det inte in i databasen. Detta är problem för spel som har ' i sig, t.ex. "Bonk's Adventure". Funderar hur jag ska göra detta, kanske någon rexexp (som jag har för att embedda youtube filmer) fast som gör att varje ' har ett \ före sig... Tips?

Permalänk
Medlem

htmlentities på strängen som ska lägga in i databasen brukar vara bra:

http://us2.php.net/htmlentities

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Kom på en till sak, som jag inte vet hur jag ska fixa. Om någon skriver in ett ' i nåt av fälten, speciellt Runner och Game, så läggs det inte in i databasen. Detta är problem för spel som har ' i sig, t.ex. "Bonk's Adventure". Funderar hur jag ska göra detta, kanske någon rexexp (som jag har för att embedda youtube filmer) fast som gör att varje ' har ett \ före sig... Tips?

Det är ett starkt tecken på att du inte gör saker "korrekt" . Jag ser i kristallkulan att du just nu kör en databasfråga direkt på användarens indata, utan att kontrollera att den inte innehåller skadlig kod (se xkcd). Du måste ha kontroll över indata i koden, annars ligger din applikation öppen för attacker. Automatiserade script på datorer världen över spenderar dag och natt med att testa alla sidor de kan hitta via sökmotorer och annat, så det är bra att inte lämna saker alltför enkla att ta över, och det är lätt att göra några första kraftfulla skyddande steg.

Till att börja med: databasfrågor bör inte gå genom `mysql`-modulen (som jag även det i kristallkulan kan se att du använder), utan `mysqli` som använder s k prepared statements. Detta ger prestandavinster (i små fall knappt märkbara) men framför allt ett gränssnitt till MySQL där databasen har en chans att veta vad som är tänkt att vara indata och vad som är querysyntax. Korrekt användning av mysqli kommer automatiskt göra att risken för SQL-injektion elimineras (så länge det används i närheten av korrekt). PHP:s dåliga säkerhetsrykte hade fått sig en ordentlig skjuts uppåt om de inte bara gjort `mysql`-tillägget "utdaterat" utan även tagit bort det helt, men samtidigt så hade de väl förlorat många användare. Det kan argumenteras för att det sistnämnda borde varit att föredra (och det verkar som att de faktiskt förbereder sig för att ta bort `mysql` helt till förmån för `mysqli`, men tidsplanen är lite oklar). Se gärna How to prevent SQL injection in PHP?

Om du använder `mysql`-tillägget så får du i stället åtminstone se till att jobba med input sanitizing, dvs se till att den data du kör i dina databasfrågor inte innehåller skadlig kod. En vanligen använd funktion i PHP för detta i samband med databaser är mysql_real_escape_string() (som dock allena ej är komplett ur säkerhetssynpunkt — se den högst rankade kommentaren på detta svar). Men som sagt: använd `mysqli` i stället, då säkerheten där kommer "på köpet" i stor utsträckning.

Utöver dessa rena SQL-baserade säkerhetsaspekter så behöver du skydda dig från XSS. Om du någonstans skriver ut data som en användare angivit så får du se till att den inte kan tolkas som HTML/JS/etc.-kod. På allt du skriver ut till dokumentet från osäkra datakällor (användares input är osäker i sammanhanget) så ska du slänga på ett htmlspecialchars() på strängen (men akta dig extra noga om du skriver ut användardata inuti en HTML-tagg, likt `<img src="<?php echo $anvandarens_egen_avatar_url;?>">`).

Och som om inte detta vore nog så behöver du även göra validering av input. Om du tar in ett värde från användaren och förväntar dig att det ska vara ett heltal: gör en (int)-typkastning på värdet (och hantera fallet då det inte går). Om du förväntar dig att det ska vara ett datum, kontrollera detta (både att det är på formen du önskar, samt att det är ett giltigt datum).

Säker webbprogrammering handlar mycket om att veta vad man får in, och veta vad man skriver ut. Om man arbetat fram den reflexen så är mycket vunnet.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

Hojj vlket helvete. Aja, då är väl sportlovet planerat

Om jag förstår rätt, så just nu gör jag
HTML -> PHP -> MYSQL

medans med MySqlI så blir det:
HTML -> PHP -> MYSQLI -> MYSQL

? Med andra ord, jag tappar inte databasen?

Permalänk
Hedersmedlem
Skrivet av lambdaN:

Hojj vlket helvete. Aja, då är väl sportlovet planerat

Om jag förstår rätt, så just nu gör jag
HTML -> PHP -> MYSQL

medans med MySqlI så blir det:
HTML -> PHP -> MYSQLI -> MYSQL

? Med andra ord, jag tappar inte databasen?

Just nu använder du den utdaterade modulen som heter Original MySQL API i PHP för att kommunicera med databasen MySQL.

I stället rekommenderas det att du använder modulen MySQLi för att kommunicera med databasen MySQL (eller PDO_MYSQL som är liknande).

Redan i PHP-manualens introduktionsavsnitt för den gamla MySQL-modulen så står det:

Citat:

This extension is deprecated as of PHP 5.5.0, and is not recommended for writing new code as it will be removed in the future. Instead, either the mysqli or PDO_MySQL extension should be used. See also the MySQL API Overview for further help while choosing a MySQL API.

Ett praktiskt exempel (inte fullständigt, inte ämnat för produktion, nätet svämmar över av mer utförlig information om skillnaderna mellan API:erna):

I stället för att du med det gamla API:t skriver en textsträng och kastar den rakt in i MySQL:s innersta via

// OBS! Exempel på hur en databasfråga INTE ska se ut: mysql_query('UPDATE tabell SET namn="'.$_POST['namn'].'", alder="'.$_POST['alder'].'" WHERE id='.$_POST['id']);

så bör/kan/ska man med MySQLi skriva likt (rikligt kommenterat):

// Definiera för MySQL vad som är frågestruktur och vad som är indata. Indata // markeras med placeholder '?'. $query=$mysqli->prepare('UPDATE tabell SET namn=?, alder=? WHERE id=?'); // Definiera vilka datatyper som ska skickas in och vilka som är motsvarande // PHP-variabler (s=sträng, i=integer=heltal). $query->bind_param('sii', $_POST['namn'], $_POST['alder'], $_POST['id']); // Nu har MySQL hunnit förbereda sig och frågan körs inte förrän nu med ett // enkelt: $query->execute(); // Om vi inte ska använda denna fråga mer så kan vi för ordnings skull köra // (detta körs när PHP är klar med sidan likväl, kan nämnas) $query->close();

MySQL kommer här "kompilera" queryn och förbereda sig för att ta emot själva frågan för att sedan köra den med "execute". Om många rader ska behandlas så snabbar detta upp saker då samma kompilerade fråga kan återanvändas (enkelt sagt; i praktiken hjälper cachefunktioner till med detta även om man inte själv säger till). Den stora fördelen är dock för de flesta bara att det nu är definierat vilka datatyper som ska in i vilka fält, om de inte stämmer så säger PHP till, och SQL-injektion är avvärjt (om man inte gör något särdeles dumt).

För att visa på sårbarheter som det första "dåliga" sättet att skriva på öppnar: låt säga att en användare i namnfältet skrivit

kalle", losenord="hej

och kanske gjort en egen POST-request med id-fältet

1 OR 1=1

Om du använder MySQLi så kommer namnet helt enkelt sättas till den strängen (inget säkerhetshål), och om `id`-fältet inte var ett heltal så skulle PHP säga till och stoppa transaktionen.

Om du skrivit som i det första naiva fallet ovan med gamla API:t så skulle användaren dels kunna ändra lösenordet genom en sidomekanism, och även möjligen (exemplet var bara en prototyp för att illustrera idén) ändra allas namn och lösenord till kalle:hej. Vad detta i praktiken gör beror på sidan i övrigt, men vi kan alla hålla med om att det inte är önskvärt.

Om det handlat om en DELETE-sats så skulle "1 OR 1=1"-liknande `id`-fält kunna rensa hela databasen.

Om det handlat om en SELECT-sats så skulle det kunna få allas uppgifter att skrivas ut till den som gjorde requesten, inklusive lösenord om de även gjorde en variant likt den i namnfältet för att påverka ytterligare fält.

Det var enkla exempel på SQL-injektion (kanske för enkla i praktiken, men de är som sagt bara tänkta att illustrera idén). För att ha ett någorlunda acceptabelt skydd med det utdaterade MySQL-API:t så skulle man kanske i stället skriva:

$namn = mysql_real_escape_string($_POST['namn']); $alder = (int)$_POST['alder']; $id = (int)$_POST['id']; mysql_query('UPDATE tabell SET namn="'.$namn.'", alder='".$alder.'" WHERE id='.$id);

Några observationer angående detta sätt:

  • det blir lika mycket att skriva i detta enkla exempel, betydligt mer om frågan är mer komplicerad (mer indata)

  • det blir mindre tydligt vad som faktiskt händer i databasfrågan (i mer komplicerade exempel är detta ett reellt problem)

  • prestandan blir sämre än med prepared statements

  • extra tid och kod behöver tillbringas på att kontrollera indata och om man gör något fel här så öppnar man för attacker (hint: alla gör någon gång något fel här).

Är man "van" vid det gamla och snart försvunna sättet så kan det kännas bökigt att byta (det tyckte jag när jag först uppmärksammades på problematiken), men snabbt märker man att det nya sättet snarare tjänar tid, är mycket mer överblickbart och man kan sova bättre om nätterna; dels av säkerhetsskäl, men även då man inte riskerar att vakna upp och se att PHP tagit bort stödet för det gamla MySQL-API:t.

Som sagt: internet har mängder med mer information. Läs t ex de länkar till Stack Overflow som jag gav i förra posten för lite inblick. Fler länkar:

Uppdaterar sista länken till archive.org då den raderats från SO.
Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.