Som fortæller noget om sikkerhed i forhold til brug af sessions.
Hvad ville være bedst? Og hvordan skal det laves?
Jeg har sørget for at sikre mod al sql-injection ved brug af mysql_real_escape_string(); når der arbejdes med $_GET[].
Jeg tænkte at man kunne lave en session.php-fil hvor al den kode der som standard skal sørge for at det er sikkert, kan inkluderes på alle sider i login systemet.
Jeg håber der er nogen der kan gøre mig lidt klogere, og være med til at gøre det sikkert! :-)
Hvis det du har lavet der, ikke er sikkert nok - er 80+% af de hjemmesider jeg har set i koden på ikke sikre nok.
Du kan ikke som bruger direkte manipulere session's på serveren - med mindre du har root access.
Du kan jo kombinere det med IP check (når man logger ind sættes der et ip nummer i database), der så checkes sammen med andre "sessions fra brugeren" - så man inden for f.eks. 5 minutter ikke kan access siden fra en anden IP end den man loggede på med sidst.
Derudover - kan du gemme en cookie på brugerens computer - som f.eks. består af:
Det var selvfølgelig også nogle gode idéer! Men jeg tænkte på f.eks. om funktionerne:
session_regenerate_id();
session_id();
(som oplyses i kode-forslagene på det link jeg skrev i det første indlæg)
Og så videre, kunne drage nogen nytte. Jeg er ikke klar over hvordan de bruges i praksis.
Det er i sær i forbindelse med persondataloven, og selvfølelig brugernes troværdighed til mit site, at det er vigtigt, at de personlige oplysninger er lagret sikkert i databasen, og ikke bare kan nuppes uden problemer. Det skal de ikke kunne! :-) Direkte hacking til databasen/serveren vil nok ikke være muligt. Surftown som jeg er hostet ved, er så vidt jeg ved, meget sikker!
Ikke at jeg tror, det betyder forfærdeligt meget med folks session. Den side du angiver siger jo også selv at PHP's session ID generator er meget random, og derfor er det super svært at ville gætte et id
Det er jo ikke fortløbende numre session består af - det er jo en virkelig lang string.
"Jeg har sørget for at sikre mod al sql-injection ved brug af mysql_real_escape_string();" >> Det er ikke en særlig sikker fremgangsmåde.
I stedet for at bruge det efterhånden forældede mysql-API, bør du kikke på prepared statements i mysqli. Det er langt sikrere - og i tilgift væsentligt hurtigere.
Tak for det olebole :-). Men jeg kan ikke sådan lige gå til objektorienteret programmering, på 0,5.. Det kræver at man sætter sig virkelig ind i det!
Jeg kan ikke se hvad der er galt med mysql_real_escape_string(); - funktionen sørger for at tegn som '," osv. bliver fjernet var strengen.
Når jeg laver en forbindelse til databasen er det:
$hent = mysql_query("SELECT * FROM mintabel ORDER BY id DESC") or die(mysql_error()); $vis = while(mysql_fetch_array($hent)) { echo $vis['blabla']; }
Sådan gør jeg det normalt. Skal jeg opdatere noget i mysql, gør jeg således:
mysql_query("UPDATE mintabel SET bla='mereblabla' WHERE id='$_GET[id]'") or die(mysql_error());
Eksempelvis. Her vil jeg så, hvis det er "offentligt" for alle brugere, sørge for at bruge mysql_real_escape_string(); til id'et, da det er noget de jo evt. kan ændre på i linket på siden.
Det ville være rart at få at vide om det er helt ude i skoven, eller om det er ok :-)
Ingen taler om, at du skal bruge mysqli objektorienteret. Hvis du bruger mere end et halvt minut på at kikke på de to links, vil du se, at API'et kan bruges både OO og proceduralt. Desuden er det pærelet at bruge det OO - og ingen siger, at du behøver at OO'e resten af din kode. Her er en tutorial, som er meget let at gå til. Prøv at kikke på den og se, hvor let det er, selvom der er brugt mysqli OO.
Det er muligt, at du ikke kan se, hvad der er galt med det gamle API. Det kan såmænd også fungere - på samme måde som tabellayout og HTML3.2 uden CSS stadig er anvendeligt.
Ikke desto mindre er det ikke uden grund, at man har konstrueret et kraftigt forbedret API (mysqli = mysql - improved), og at ingen professionelle løsninger idag bliver skrevet i det gamle API. Mysqli er både langt sikrere og væsentligt hurtigere.
Mysql har altid været anset for 'mindre lødig' i forhold til 'de store' såsom mssql og oracle - ikke mindst p.gr.a. manglen på parametriserede kald og deraf dårligere sikkerhed.
Den tid er endelig forbi, men om man så vil fortsætte med at 'fedte rundt' med det gamle API, er naturligvis ens eget valg.
Ja, din update er skoleeksemplet på rent selvmord! Du hjælper den, der vil tømme din DB så effektivt, som det overhovedet kan gøres.
Specielt når man ikke kan overskue, hvad man gør, bør man lade databasen om selv at escape, hvad der skal escapes - og det kan du tydeligvis ikke =)
Din update kræver, at du helt 100% sikkert husker at bruge mysql_real_escape_string. Det viser al erfaring for det første, at du ikke gør - og *BANG*, så er du død!
Desuden er DB'ens egen escaping mere effektiv - og du undgår de uheldige sideeffekter, din fremgangsmåde ellers byder på. The_Buzz og jeg har således lige været forbi en af dem tidligere i dag.
/* Lukker: the statement */ $opret_bruger->close();
} else { /* Fejl */ printf("Der opstod en fejl i Prepared Statement: %s\n", $mysqli->error); }
Jeg har ikke helt fået fat i, hvad s,d,i,b skal gøre. Jeg skrev først ssd, da aktiveringslinket jo er tal. Men så fik jeg et helt andet tal, end det der var i strengen. Men det virker nu med 'sss'.
Ser det ellers rigtigt ud? Og er det samme princip med update, select m.m?
Ellers kan jeg godt se fidusen i det.
Jeg er godt klar over, at med en ubehandlet $_GET[] ind i en mysql_query vil være at skyde sig selv i foden - jeg ved ikke hvordan du i teorien vil kunne tømme databasen i min update.. - Men hvis den først får mysql_real_escape_string(); f.eks. sådan her:
$id = mysql_real_escape_string($_GET[id]); mysql_query("UPDATE mintabel SET bla='mereblabla' WHERE id='$id'") or die(mysql_error());
Så er den vel forholdsvis sikker? Men jeg kan stadigvæk godt se det smarte ved Prepared Statements.
En lidt anden ting. Jeg kan hurtigt komme til at lave mange mysql_query SELECT og UPDATE i en fil. I slutningen af filen skriver jeg mysql_close(); og går ud fra at alle forbindelserne er lukkede. Er det en fejlantagelse?
Hvis jeg samtidigt skal opdatere noget i flere database, så gør jeg det blot:
mysql_query("UPDATE tabel_a SET bla = 'blablab'") or die(mysql_error());
mysql_query("UPDATE tabel_b SET bla = 'blabla'") or die(mysql_error());
mysql_query("UPDATE tabel_c SET bla = 'blabla'") or die(mysql_error());
Jeg har prøvet at hente mine data med prepared statements. Men det fungerer ikke.
Min kode: $load = mysql_query("SELECT * FROM * WHERE email = '$_SESSION[email]'") or die(mysql_error()); $show = mysql_fetch_array($load);
TIL:
/* Create the prepared statement */ if ($stmt = $mysqli->prepare("SELECT FirstName,LastName FROM CodeCall ORDER BY LastName")) { /* Execute the prepared Statement */ $stmt->execute();
/* Bind results to variables */ $stmt->bind_result($firstName, $lastName);
Mine koder kommer godt nok til at fylde meget mere på den måde. Men hvis det er sikkert er det vel det værd!! :-)
Jeg skal ikke have lavet en while() på den her, så hvordan får jeg den til at lave et array i stedet $show[]... Det skal jeg bruge flere gange i mit script nemlig..
Lad os lige tage én ting ad gangen (over flere indlæg) - og det tyder ikke på, du endnu har forstået, hvad fordelene er ved prepared statements (herefter kaldet PS) eller, hvad bagdelene er ved mysql_real_escape_string.
Med den sidste får du problemer med legale apostroffer - f.eks. bliver Tim O'Reilly til Tim O\'Reilly. Ligesom PHP i mange tilfælde i forvejen escaper med backslashes - med deraf dobbelt escaping tilfølge.
Derudover er du ikke beskyttet mod SQL-injection ved multibyte tegnsæt - bortset fra utf-8. Din løsning er altså ikke særlig portabel. Da du ydermere kun anvender den på strenge, står du jo stadig pivåben for injections ved talfelter.
Derudover viser al erfaring, at du med garanti glemmer mysql_real_escape_string på et tidspunkt - på et sted og tidspunkt, hvor det har katastrofale konsekvenser.
mysql_real_escape_string beskytter dig derfor somme tider - andre gange gør den ikke! Lidt som at spille Lotto ... eller måske nærmere: Russisk roulette! *o)
Det gamle API bygger på, at du 'hækler' strenge sammen til et SQL-kald, som sendes til DB'en. Det er i udgangspunktet en potentielt farlig og elendig løsning.
Med PS kommer dine variabler aldrig i nærheden af et SQL-udtryk. Dit statement opbygger en procedure, som gemmes i DB'en, og når du efterfølgende sender variabler som parametre til denne procedure, indgår de ikke i en SQL-streng. Proceduren afgør, om parametren passer med det forventede og escaper den i forhold til det af DB'en benyttede tegnsæt. Parametren kan indeholde, hvad somhelst. Er det andet end det forventede, forkastes kaldet bare.
Alt dette friholder dig naturligvis ikke for at tjekke alt, hvad der ankommer 'udefra'. Alt, hvad du ikke selv har skrevet, skal tjekkes for, om det præcist svarer til, hvad du forventer. I den forbindelse nytter det ikke noget, du whiner over at skulle skrive flere linjer kode. Ja, gu' fylder det, men det handler ikke om udviklerens niveau af dovenskab - det handler om hans kundes og dennes brugeres sikkerhed *o)
Feltets datatype afgør, om du skal bruge s, i, d eller b - ikke, hvilke tegn variablen/parametren indeholder. Skal værdien "123.456" indsættes i et VARCHAR felt, skal du således bruge s, uagtet det ligner et tal.
"Jeg er godt klar over, at med en ubehandlet $_GET[] ind i en mysql_query vil være at skyde sig selv i foden" >> Både og! Du skal som sagt altid tjekke, om variablen stemmer præcist med, hvad du forventer at modtage. Den er til gengæld ikke til fare i injection sammenhæng. Derfor skal du ikke bruge nogen form for escaping af dén grund.
- og så skal olebole have brygget lidt kaffe. Resten følger =)
Nu roder du mysql og mysqli sammen. $opret_bruger->close(); lukker dit statement. Din forbindelse$mysqli er stadig åben.
Skal du opdatere, opretter du et passende mysqli-statement og eksekverer det. Da du ikke kan binde tabelnavnet til en parameter, må du oprette og lukke et statement til hver update. Du kan gøre det med et komplekst statement - men lad være med at koncentrere dig om det nu. Du har rigeligt at se til i øjeblikket *o)
/* Create the prepared statement */ if ($stmt = $mysqli->prepare("SELECT FirstName,LastName FROM CodeCall ORDER BY LastName")) { /* Execute the prepared Statement */ $stmt->execute();
/* Bind results to variables */ $stmt->bind_result($firstName, $lastName);
/* Create array to hold the recordset */ $rows = array();
Man kan bruge en lidt fiksere fremgangsmåde ved brug af klassen ReflectionMethod og dennes invokeArgs metode, men det er lidt mere kompliceret, da det kræver, at man har godt styr på at sende argumenter 'by reference'.
Glem det på nuværende tidspunkt og koncentrer dig om at lære det grundlæggende - og blive godt dus med dét. Så kan du altid udvikle dine kompetencer og lave lidt mere elegante løsninger =)
PS: Nu, vi taler sikkerhed, bør du - når koden går live - slette dine printf's af fejl. Du bør også søge for, at PHP's fejlmeddelelser fra - eller sætte dem ned til et minimum. Evt. ved at bruge error_reporting i en includefil i toppen af hvert dokument. Her kan du så skifte mellem alle eller ingen/få meddelelser alt efter, om du er i udvikling eller ej.
Du kan også sætte det i php.ini, hvis du adgang til den.
Det var så sidste indlæg i denne omgang, men du stiller bare uddybende spørgsmål, hvis du har nogen =)
Mange tak for de, som altid, fyldestgørende svar! :-)
Jeg har fået det til at virke med INSERT og SELECT. For at forstå hvordan man undgår sql-injections, m.m. så bør man vel vide hvordan de laves? Har du et link el. lign. som bare fortæller lidt om det? ( Ikke fordi jeg er ude på at rydde nogen database :-P )
Min UPDATE, kommer ikke med fejl, men databasen registrere heller ingenting.
Har du nogen idé om hvad der er galt? (har du sikkert). Hvordan laver man desuden en DELETE-query? Når dette er løst, så tror jeg at det kommer til at spille =)
mysql_real_escape_string hjælper dig ikke med noget. htmlspecialchars kan være yderst fornuftig, men ved tal nøjes du med at tjekke, om der er tale om et tal - eller type'er variablen med (int). I det hele taget skal du som sagt tjekke variablerne for, om de indeholder, hvad du forventer.
Jeg tvivler på, du skal bruge s til alle dine parametre, Prøv:
Måske, du vil få en fejl med 'error linjen', hvis du flytter den ned efter din execute. I hvertfald skal jeg lede meget længe i din kode for atfinde de variabler, du bruger i:
if ($opdater_bruger->errno) echo $opdater_bruger->error.'<br>'; // s=String, d=decimal, i=Integer, b=blob (sent in packets) - afhænger af om det varchar (s), decimal (d) osv. i databasen!
/* Lukker: the statement */ $opdater_bruger->close(); mysql_close($mysqli); // Lukker forbindelse fra $mysqli->prepare
Der var rigtig nok nogle variabler der ikke passede sammen med ovenstående som er behandlet med htmlspecialchars().. Men det skulle gerne være rettet nu. Derudover har jeg ændret den sidste til i, fordi ID'et jo er int()-format i databasen.
Ja, men din UPDATE kan tydeligvis ikke finde den. Ligger de to kald i hver sin funktion? I så fald er varibler jo lokale.
Din SELECT lægger rækkerne i array'et $rows, så den ligger typisk som $rows[n]['id'] (hvor n er et tal). Jeg kender dog ikke strukturen i din kode, så jeg ved ikke, om din UPDATE kan 'se' $rows.
PS: Husk i øvrigt altid at bruge apostroffer i array kald, når indekset er en streng. Ikke $_POST[fnavn], men $_POST['fnavn'] - Array do's and don'ts
<?php if($dag AND $maaned AND $aar != "") { $alder = birthday("$aar-$maaned-$dag");
echo "<p class='birthday-tekst'>Vi har allerede registreret at du er $alder år gammel, og at din fødselsdagsdato er $dag/$maaned-$aar :-)</p><br><br>";
Jeg har ikke haft tid til at kikke på tråden før nu. Ja, det lukker forbindelsen, hvilket selvfølgelig var årsagen til problemet. Godt, du fandt det =)
#33 >> Eksperten går i knæ hver nat omkring kl. 2-3 stykker. Af uforståelige årsager er det åbenbart ikke muligt at holde applikationen kørende uden en større, natlig opdatering af databaser. Derfor opstår der ofte mærkelige ting med forsvindende inlæg i nattens løb :o|
Jeg har nogle variabler som skal sættes ind, selvom de er ligmed 0. Når værdien er 0, er det som om at den ikke vil sætte dem ind. Det virker fint med det gamle mysql_query(insert into blabla....), men det virker altså ikke med prepared statements, så kan du måske sige mig hvad løsningen er?
Er kun en test for at vise, hvad du indsætter. Resultatet, som de udskriver, viser, at din variabel $type er NULL. Prøv at sætte den til et eller andet 'foo' eller 'bla' eller whatever. Og prøv så, om der indsættes noget
Jeg lavede det bare ligesom ved de andre to, med en insert_null_val, hvis der ingenting er. Jeg har også prøvet at sætte den ligmed blabla, men det bliver stadigvæk ikke sat ind i databasen - og jeg har prøvet at sætte dem alle lig blabla (alle dem der er sat til insert_null_vall).
Hvis der er tale om en variabel, så savner jeg et $ - og det er jeg ret sikker på, PHP også gør. Der kan dog også være tale om en defineret konstant, men dem plejer man jo at skrive med stort.
Det undrer mig nu også lidt, at f.eks. feltet orderamount er af typen VARCHAR. Brug de datatyper, der passer til det, felterne skal indeholde - og angiv kun den længde, de skal indeholde. Skal du indsætte et fornavn, er det spild af plads at afsætte mulighed for at indsætte 255 tegn. Det er nok sjældent at skulle håndtere navne som Herbert Ludwig Leopold Maximillian van der Schwandvogel III *o)
Jeg har forsøgt at gøre det med gåseøjne, men det virker ikke. Det virker først når det bliver sat ind via. en variabel.
Ja, jeg skal bestemt have kigget lidt på databasen, tak :-)
Der er en ting der undre mig lidt. Når jeg laver en while: while($hent_data->fetch()) { $vis_data[] = array('pointprivat'=>$pointprivat, 'type'=>$type, 'kampagneid'=>$kampagneid, 'dato'=>$dato, 'tid'=>$tid); }
Hvis jeg fjerner $vis_data[] = array(); osv. så jeg kun har while($hent_data->fetch()) { } tilbage, så virker det stadigvæk, uden problemer. Hvordan kan det være? Så kan jeg vel undlade det? Eller har det en anden funktion?
Linjen lægger alle fundne rækker ind i array'et $vis_data. Det betyder, du efterfølgende kan hente forskellige rækker fra array'et - f.eks. kan du udskrive feltet kampagneid i den tredie række:
Hvad hvis jeg eksempelvis har en række artikler der skal vises, dvs. alle 10 artikler på en gang.
Det plejer jeg jo at lave:
$show = mysql_fetch_array($query)) {
$show['artikelnavn']; blabla...
}
Hvordan gør jeg det nu? Det virker fint hvis jeg laver whilen gå ned over det jeg gerne vil have vist alle rækker af. Men lige så snart at jeg laver en ny forbindelse indeni den while med endnu en while, så dur det ikke.
Problemet er at jeg skal hente noget fra transaktioner og så vises alle transaktionerne, men der skal også navn på hvad det er for nogle transaktioner, og der skal kampagner-databasen så ind i billedet. Den vil ikke gå med til at jeg lave en while der også.. Den henter ingen data så.
<?php if($hent = $mysqli->prepare("SELECT `pointprivat`, `type`,`kampagneid`, `dato`, `tid` FROM transaktioner WHERE bruger=? AND status=? ORDER BY id DESC")) {
//$hent = mysql_query("SELECT * FROM transaktioner WHERE bruger = '$id' AND status = 'godkendt' ORDER BY id DESC") or die(mysql_error()); //while($vis = mysql_fetch_array($hent)) {
if($hent_kampagner = $mysqli->prepare("SELECT `navn` FROM kampagner WHERE cpid=?")) {
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.