Og den minste hjelpen ville være fra funksjonen du utviklet. PHP: \"Sitater\". Skrive mysql-spørringer, skråstreker, unnslippe sitater Søke i kode

sitatstreng (11)

Jeg prøver å finne ut den beste måten å skrive spørsmål på. Jeg forstår også viktigheten av å være konsekvent. Så langt har jeg tilfeldig brukt enkle anførselstegn, doble anførselstegn og backticks uten noen reell tanke.

$query = "INSERT I tabell (id, col1, col2) VERDIER (NULL, val1, val2)";

I eksemplet ovenfor bør du også vurdere at "tabell", "kol[n]" og "val[n]" kan være variabler.

Hva er standarden for dette? Hva gjør du?

Jeg har lest svar på lignende spørsmål i omtrent 20 minutter, men det ser ikke ut til å være et definitivt svar på dette spørsmålet.

Svar

Anta nå at du bruker direkte post-variabel i MySQL-spørringen, så bruk den slik:

$query = "SETT INN I `tabell` (`id`, `navn`, `e-post`) VERDIER (".$_POST["id"]." ", " ".$_POST["navn"]." ", " ".$_POST["e-post"].." ")";

Dette er den beste praksisen for bruk av PHP-variabler i MySQL.

Hovedsakelig i Mysql, brukes disse typer identifikatorer i ` , " , " og ()-spørringer.

    " eller " bruk for å inkludere en streng som verdien "01/26/2014 00:00:00" eller "01/26/2014 00:00:00" . Denne identifikatoren brukes bare for strengfunksjonen "01/26/2014 00:00:00", for eksempel now() eller sum ,max .

    ` bruk for å inkludere en tabell eller tabelltabell, for eksempel velg kolonnenavn fra tabellnavn der id = "2"

    () brukes bare for å bare omslutte deler av en spørring, for eksempel velg kolonnenavn fra tabellnavn hvor (id = "2" og kjønn = "mann") eller navn = "rakesh.

Bortsett fra alle (godt forklart) svarene, var det ingen nevnt nedenfor, og jeg kommer ofte til denne spørsmål og svar.

I et nøtteskall; MySQL tror du vil gjøre matematikk på din egen tabell/kolonne og tolk bindestreker som "e-post" som e-post .

Ansvarsnektelse. Så jeg tenkte å legge til dette som et "FYI"-svar for de som er helt ferske i å jobbe med databaser, og som kanskje ikke forstår de tekniske termene som allerede er beskrevet.

(Det er gode svar ovenfor angående SQL-karakteren til spørsmålet ditt, men dette kan også være relevant hvis du er ny i PHP.)

Det kan være viktig å merke seg at PHP behandler enkle og doble anførselstegn forskjellig...

Enkeltsiterte strenger er "bokstavelige" og er ganske mange WYSIWYG-strenger. Dobbeltsiterte strenger tolkes av PHP for mulig variabelerstatning (tilbakereferanser i PHP er ikke akkurat strenger, de utfører kommandoen i skallet og returnerer resultatet).

$foo = "bar"; ekko "det er en $foo"; // Det er et $foo ekko "det er en $foo"; // Det er en bar ekko `ls -l`; // ... en katalogliste

Hvis kolonnetabellene og verdiene er variabler, er det to måter:

Med doble anførselstegn "" er hele spørringen:

$query = "SETT INN I $tabellnavn (id, $col1, $col2) VERDIER (NULL, "$val1", "$val2"");

$query = "INSERT I ".$table_name." (id, ".$col1.", ".$col2.") VERDIER (NULL, "".$val1.", "".$val2."" ) ";

Med enkle anførselstegn "" :

$query = "SETT INN I ".$tabellnavn." (id, ".$col1.", ".$col2.") VERDIER (NULL, ".$val1.", ".$val2.")";

Bruk backticks `` når kolonnen/verdinavnet ligner på et MySQL-reservert nøkkelord.

Merk. Hvis du spesifiserer et kolonnenavn med et tabellnavn, bruk backticks som dette:

`tabellnavn` . "kolonne_navn".<- Примечание: исключить. из задних клещей.

Backticks bør brukes for tabell- og kolonneidentifikatorer, men er kun nødvendig når identifikatoren er et MySQL-reservert nøkkelord eller når identifikatoren inneholder mellomromstegn eller tegn utenfor det begrensede settet (se nedenfor). Det anbefales ofte å unngå å bruke reserverte nøkkelord som kolonne- eller tabellidentifikatorer hvis mulig for å unngå sitatproblemet.

Enkelte anførselstegn skal brukes for strengverdier, for eksempel i VALUES()-listen. Doble anførselstegn støttes av MySQL for strengverdier også, men enkle anførselstegn er mer allment akseptert av andre RDBMS-er, så det er en god idé å bruke enkle anførselstegn i stedet for doble anførselstegn.

MySQL forventer også at DATE- og DATETIME-literelle verdier er enkeltsitert som strenger, for eksempel "2001-01-01 00:00:00" . For mer informasjon, se litteraturdokumentasjonen for dato og klokkeslett, spesielt alternativer til å bruke bindestreken - som segmentskilletegn i datostrenger.

Så ved å bruke eksemplet ditt, ville jeg doble PHP-strengen og bruke enkle anførselstegn for verdiene "val1", "val2" . NULL er et MySQL nøkkelord og en ikke-verdi og brukes derfor ikke.

Ingen av disse tabell- eller kolonneidentifikatorene er reserverte ord eller bruker tegn som krever sitering, men jeg siterte dem med baklengs uansett (mer om det senere...).

RDBMS-relaterte funksjoner (som NOW() i MySQL) skal ikke siteres, selv om argumentene deres er underlagt de samme reglene eller sitatreglene som allerede er nevnt.

Backtick(`) tabell og kolonne ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ─ INSERT INTO `table` (`id`, `col1`, `col2`, `date`, `updated`) VERDIER (NULL, "val1", "val2", "2001-01-01", NOW())"; Nøkkelord uten anførselstegn ─────┴┴┴┘ │ │ │ │ │ │ │││││ Enkeltsiterte (") strenger ────────────── ───┴── ┴────┘ │ │ │││││ Enkeltsitert (") DATO ──────────────────── ───┴── ───────────────────────── ─────────── ──┴┴┴┴┘

Variabel interpolasjon

Anførselsmønstre for variabler endres ikke, men hvis du har tenkt å interpolere variabler direkte i en streng, må den angis med doble anførselstegn i PHP. Bare sørg for at du unnslipper variabler riktig for bruk i SQL. (Det anbefales å bruke et API som støtter forberedte setninger i stedet som et forsvar mot SQL-injeksjon.)

// Samme ting med noen variabelerstatninger // Her er et variabeltabellnavn $table angitt med backtick, og variabler // i VALUES-listen er enkeltsitert $query = "INSERT INTO `$tabell`(`id`, `col1`, `col2`, `date`) VERDIER (NULL, "$val1", "$val2", "$date")";

Utarbeidede uttalelser

Når du arbeider med utarbeidede utsagn, se dokumentasjonen for å avgjøre om utfyllende utsagn skal inkluderes. De mest populære APIene tilgjengelig i PHP, PDO og MySQLi involverer uautorisert plassholdere, som de fleste forberedte instruksjons-API-er på andre språk:

// PUD-eksempel med navngitte parametere, unquoted $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VALUES (:id, :col1, :col2, :date)" ; // MySQLi eksempel med ? parameters, unquoted $query = "INSERT INTO `tabell` (`id`, `col1`, `col2`, `date`) VERDIER (?, ?, ?, ?)";

Symboler som returnerer en tilbakereferanse i identifikatorer:

For eksempel:

Det samme kan gjøres for tabellnavn og feltnavn. Dette er veldig god vane hvis du binder database-ID-en din med bakvinduer.

Sjekk ut dette svaret for å lære mer om omvendte slutninger.

Nå om doble anførselstegn og enkle anførselstegn (Michael har allerede nevnt dette).

Men for å definere verdien, må du bruke enkle eller doble anførselstegn. La oss se et annet eksempel.

INSERT INTO `tabellnavn` (`id, `tittel`) VERDIER (NULL, tittel1);

Her glemte jeg bevisst å pakke tittel1 inn i anførselstegn. Serveren vil nå godta tittel1 som kolonnenavn (dvs. identifikator). Så for å indikere at dette er en verdi, må du bruke doble eller enkle anførselstegn.

INSERT INTO `tabellnavn` (`id, `tittel`) VERDIER (NULL, "tittel1");

Nå, kombinert med PHP, gjør doble anførselstegn og enkle anførselstegn det mye enklere å skrive spørsmål. La oss se på den endrede versjonen av spørringen i spørsmålet ditt.

$query = "SETT INN I `tabell` (`id`, `col1`, `col2`) VERDIER (NULL, "$val1", "$val2"");

Nå, ved å bruke doble anførselstegn i PHP, vil du få variablene $val1 og $val2 til å bruke sine verdier, og dermed lage en gyldig spørring. som

$val1 = "min verdi 1"; $val2 = "min verdi 2"; $query = "SETT INN I `tabell` (`id`, `col1`, `col2`) VERDIER (NULL, "$val1", "$val2"");

INSERT INTO `tabell` (`id`, `col1`, `col2`) VERDIER (NULL, "min verdi 1", "min verdi 2")

Det var mange nyttige svar her, som vanligvis kulminerte i to punkter.

  1. BACKTICKS (`) brukes rundt identifikasjonsnavn.
  2. ENKEL SITAT (") brukes rundt verdier.

Og som @MichaelBerkowski sa

Backticks bør brukes for tabell- og kolonneidentifikatorer, men er kun nødvendig når identifikatoren er et MySQL-reservert nøkkelord eller når identifikatoren inneholder mellomromstegn eller tegn utenfor det begrensede settet (se nedenfor). Det anbefales ofte å unngå å bruke reserverte nøkkelord som kolonne- eller tabellidentifikatorer hvis mulig for å unngå sitatproblemet.

Det er et tilfelle der identifikatoren ikke kan være det reservert søkeord eller inneholde mellomromstegn eller tegn utenfor det begrensede settet men krever definitivt tilbakekoblinger rundt dem.

123E10 er et gyldig identifikatornavn, men også et gyldig HELTAL-literal.

[Uten å gå i detalj hvordan du ville få et id-navn som det] La oss si at jeg vil lage en midlertidig tabell kalt 123456e6.

Ingen FEIL på backticks.

DB > opprett midlertidig tabell `123456e6` (`id` char (8)); Søk OK, 0 rader påvirket (0,03 sek)

FEIL hvis du ikke bruker tilbakeringinger.

DB > opprett midlertidig tabell 123451e6 (`id` char (8)); FEIL 1064 (42000): Du har en feil i SQL-syntaksen; sjekk bruksanvisningen som tilsvarer din MariaDB-serverversjon for riktig syntaks som skal brukes nær "123451e6 (`id` char (8))" på linje 1

Imidlertid er 123451a6 et fint ID-navn (uten backticks).

DB > opprett midlertidig tabell 123451a6 (`id` char (8)); Søk OK, 0 rader påvirket (0,03 sek)

Dette er helt fordi 1234156e6 også er et eksponentielt tall.

Enkelte anførselstegn skal brukes for strengverdier, for eksempel i VALUES()-listen.

Backticks brukes vanligvis for å indikere en identifikator og kan også være trygge på grunn av sporadisk bruk av reserverte nøkkelord.

Når de kombineres med PHP og MySQL, forenkler doble anførselstegn og enkle anførselstegn betraktelig skrivetiden for spørringer.

Det er to typer sitater i MySQL:

  1. " for å inkludere strengbokstaver
  2. ` for å inkludere identifikatorer som tabell- og kolonnenavn

Og så er det "dette er et spesielt tilfelle. Det kan brukes til en av målene ovenfor om gangen avhengig av serverens sql_mode:

  1. Misligholde"karakter" kan brukes til å neste streng bokstaver "
  2. I ANSI_QUOTES-modus " kan symbolet brukes til å inkludere identifikatorer, ANSI_QUOTES

Følgende spørring vil gi forskjellige resultater (eller feil) avhengig av SQL-modus:

VELG "kolonne" FRA tabell HVOR foo = "bar"

ANSI_QUOTES deaktivert

Spørringen vil velge strengen bokstavelig "kolonne" der kolonne foo er lik strengen "bar"

Aktivert ANSI_QUOTES

Spørringen vil velge kolonnekolonne der kolonne foo er lik kolonne

Når du skal bruke

  • Jeg foreslår at du unngår å bruke " slik at koden din ikke er avhengig av SQL-moduser
  • Inkluder alltid identifikatorer siden dette er god praksis (ganske mange spørsmål om SO diskutere dette)

Det er en klar forskjell mellom bruken av " " og " " .

Når " " brukes gjennomgående, er det ingen "transformasjon eller oversettelse". Den skrives ut som den er.

Med " " blir alt det omgir "oversatt eller transformert" til sin verdi.

Det jeg mener med oversettelse/konvertering er dette: alt som finnes i enkle sitater vil ikke bli "oversatt" til verdiene deres. De vil bli akseptert fordi de er innenfor anførselstegn. Eksempel: a=23 , deretter vil ekko "$a" generere $a på standard utgang. Mens ekko "$a" vil produsere 23 på standard utgang.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — Escapes spesialtegn i en streng for bruk i en SQL-setning

Beskrivelse

mysql_real_escape_string (streng $unescaped_string [, ressurs $link_identifier = NULL]): streng

Escapes spesialtegn i unescaped_stringen, tar hensyn til gjeldende tegnsett i forbindelsen slik at det er trygt å plassere det i en mysql_query(). Hvis binære data skal settes inn, må denne funksjonen brukes.

mysql_real_escape_string() kaller opp MySQLs bibliotekfunksjon mysql_real_escape_string, som legger omvendt skråstrek foran følgende tegn: \x00, \n, \r, \ , " , " og \x1a.

Denne funksjonen må alltid (med få unntak) brukes for å gjøre data trygge før du sender en spørring til MySQL.

Forsiktighet

Sikkerhet: standard tegnsett

Tegnsettet må settes enten på servernivå, eller med API-funksjonen mysql_set_charset() for at det skal påvirke mysql_real_escape_string() . Se konseptdelen om tegnsett for mer informasjon.

Parametere

unescaped_string

Snoren som skal unnslippes.

Link_identifier

MySQL-tilkoblingen. Hvis lenkeidentifikatoren ikke er spesifisert, åpnes den siste lenken av mysql_connect() er antatt. Hvis ingen slik kobling blir funnet, vil den prøve å opprette en som om mysql_connect() ble oppringt uten argumenter. Hvis ingen tilkobling blir funnet eller etablert, an E_ADVARSEL nivåfeil genereres.

Returverdier

Returnerer den escapede strengen, eller FALSK på feil.

Feil/unntak

Utførelse av denne funksjonen uten en MySQL-tilkobling vil også sendes ut E_ADVARSEL nivå PHP-feil. Utfør denne funksjonen kun med en gyldig MySQL-tilkobling til stede.

Eksempler

Eksempel #1 Enkelt mysql_real_escape_string() eksempel

//Koble
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
ELLER dø(mysql_error());

//Spørsmål
$query = sprintf ( "VELG * FRA brukere WHERE bruker="%s" OG passord="%s"",
mysql_real_escape_string($user),
mysql_real_escape_string($passord));
?>

Eksempel #2 mysql_real_escape_string() krever et tilkoblingseksempel

Dette eksemplet viser hva som skjer hvis en MySQL-tilkobling ikke er tilstede når du kaller denne funksjonen.

Eksemplet ovenfor vil gi noe som ligner på:

Advarsel: mysql_real_escape_string(): Ingen slik fil eller katalog i /this/test/script.php på linje 5 Advarsel: mysql_real_escape_string(): En kobling til serveren kunne ikke etableres i /this/test/script.php på linje 5 bool(false) string(41) "SELECT * FROM actors WHERE last_name = """

Eksempel #3 Et eksempel på SQL Injection Attack

// Vi sjekket ikke $_POST["passord"], det kan være hva som helst brukeren ville ha! For eksempel:
$_POST [ "username" ] = "hjelpemiddel" ;
$_POST [ "password" ] = "" ELLER ""="" ;

// Spørr database for å sjekke om det er noen matchende brukere
$query = ( $_POST [ "brukernavn" ]) " OG passord=" ( $_POST [ "passord" ]) "" ;
mysql_query($query);

// Dette betyr at spørringen sendt til MySQL vil være:
echo $query ;
?>

Spørringen sendt til MySQL:

Dette vil tillate alle å logge på uten et gyldig passord.

Notater

En MySQL-tilkobling kreves før bruk mysql_real_escape_string() ellers en nivåfeil E_ADVARSEL genereres, og FALSK er returnert. Hvis link_identifier ikke er definert, brukes den siste MySQL-tilkoblingen.

Merk: mysql_real_escape_string() slipper ikke unna % og _ . Disse er jokertegn i MySQL hvis de kombineres med SOM, STIPEND, eller OPPHAV.

8 år siden

Bare en liten funksjon som etterligner den originale mysql_real_escape_string, men som ikke trenger en aktiv mysql-tilkobling.Kan implementeres som en statisk funksjon i en databaseklasse.Håper det hjelper noen.

funksjon mysql_escape_mimic ($inp) (
if(er_matrise($inp))
return array_map (__METHOD__ , $inp );

If(!empty($inp ) && is_string ($inp )) (
return str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """ , "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"" , "\\"" , "\\Z" ), $inp );
}

Returner $inp ;
}
?>

13 år siden

Legg merke til at mysql_real_escape_string ikke setter tilbake skråstreker foran \x00, \n, \r og og \x1a som nevnt i dokumentasjonen, men erstatter faktisk tegnet med en MySQL akseptabel representasjon for spørringer (f.eks. \n erstattes med "\ n" bokstavelig). (\, ", og " er escaped som dokumentert) Dette endrer ikke hvordan du skal bruke denne funksjonen, men jeg synes det er greit å vite.

6 år siden

Ingen diskusjon om rømming er komplett uten å fortelle alle at du i utgangspunktet aldri bør bruke ekstern input for å generere tolket kode. Dette gjelder SQL-setninger, eller noe du vil kalle en hvilken som helst "eval"-funksjon på.

Så i stedet for å bruke denne forferdelig ødelagte funksjonen, bruk parametrisk forberedte utsagn i stedet.

Ærlig talt, bruk av brukerlevert data til å komponere SQL-setninger bør betraktes som profesjonell uaktsomhet, og du bør holdes ansvarlig av din arbeidsgiver eller klient for ikke å bruke parametrisk forberedte uttalelser.

Hva betyr det?

Det betyr i stedet for å bygge en SQL-setning som dette:

"INSERT INTO X (A) VALUES(".$_POST["a"].)"

Du bør bruke mysqli's prepare() funksjon () for å utføre en setning som ser slik ut:

"SETT INN I X (A) VERDIER(?)"

NB: Dette betyr ikke at du aldri skal generere dynamiske SQL-setninger. Det betyr at du aldri skal bruke brukerlevert data til å generere disse setningene. Eventuelle brukerlevert data skal sendes gjennom som parametere til setningen etter at den har blitt forberedt.

Så hvis du for eksempel bygger opp et lite rammeverk og ønsker å sette inn en tabell basert på forespørsels-URI, er det i din interesse å ikke ta $_SERVER["REQUEST_URI"]-verdien (eller noen del av det) og koble det direkte sammen med søket ditt. I stedet bør du analysere den delen av $_SERVER["REQUEST_URI"]-verdien du ønsker, og kartlegge den gjennom en slags funksjon eller assosiativ matrise til en ikke-bruker gitt verdi. Hvis kartleggingen ikke gir noen verdi, vet du at noe er galt med dataene som brukeren har levert.

Å unnlate å følge dette har vært årsaken til en rekke SQL-injeksjonsproblemer i Ruby On Rails-rammeverket, selv om det bruker parametrisk forberedte setninger. Dette er hvordan GitHub ble hacket på et tidspunkt. Så ingen språk er immun mot dette problemet. Det er derfor dette er en generell beste praksis og ikke noe spesifikt for PHP, og hvorfor du VIRKELIG bør ta den i bruk.

Du bør også fortsatt gjøre en form for validering av dataene som er levert av brukere, selv når du bruker parametrisk forberedte utsagn. Dette er fordi at brukeroppgitte data ofte vil bli en del av generert HTML, og du vil sikre at brukerens oppgitte data ikke kommer til å forårsake sikkerhetsproblemer i nettleseren.

9 år siden

Det er en interessant finurlighet i eksempel #2 om SQL-injeksjon: AND prioriteres over OR, så den injiserte spørringen kjøres faktisk som WHERE (bruker="aidan" OG passord="") ELLER ""="", så i stedet ved å returnere en databasepost som tilsvarer et vilkårlig brukernavn (i dette tilfellet "aidan"), vil det faktisk returnere ALLE databaseposter. Uten spesiell rekkefølge. Så en angriper kan være i stand til å logge på som en hvilken som helst konto, men ikke nødvendigvis med en hvilken som helst konto. kontroll over hvilken konto det er.

Selvfølgelig kan en angrepspotensial ganske enkelt endre parameterne sine for å målrette mot spesifikke brukere av interesse:

//F.eks. angriperens verdier
$_POST [ "username" ] = "" ;
$_POST["passord"] = "" ELLER bruker = "administrator" OG "" = "";

// Feilformat søk
$query = "VELG * FRA brukere WHERE bruker="$_POST [ brukernavn ] " OG passord=" $_POST [ passord ] "" ;

echo $query ;

// Spørringen sendt til MySQL vil lese:
// SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
// som vil tillate alle å få tilgang til kontoen som heter "administrator"

?>

1 år siden

@feedr
Jeg utdypet notatet hans slik:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$array1 = array("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$array2 = array("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
echo($streng);
echo(PHP_EOL);
for($i=0; $i if ($i==0)
$p = "/(?ellers
$p = "/(?echo($i);
echo($p);
echo($array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo("\t");
echo($streng);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($streng);

2 år siden

For å sitere Sam på Numb Safari

[ "Ingen diskusjon om escape er komplett uten å fortelle alle at du i utgangspunktet aldri bør bruke ekstern input for å generere tolket kode. Dette gjelder for SQL-setninger, eller noe du vil kalle noen form for "eval"-funksjon på.

Så i stedet for å bruke denne forferdelig ødelagte funksjonen, bruk parametrisk forberedte utsagn i stedet.

Ærlig talt, bruk av brukerlevert data til å komponere SQL-setninger bør betraktes som profesjonell uaktsomhet, og du bør holdes ansvarlig av din arbeidsgiver eller klient for ikke å bruke parametrisk forberedte uttalelser." ]

Sam har rett........

Men jeg tror ikke det er fornuftig å stoppe all desinfisering og bare gi oppgaven videre til parametrisk forberedte uttalelser.

En bestemt utvikler som jobber i en bestemt situasjon vil alltid vite mer om gyldig input (spesifikt for den konteksten).

Hvis du ber en bruker om å sende inn en verdi du allerede har gitt dem og du vet at alle slike verdier starter AB****** og strengen skal være av lengde 7 eller 11, men aldri noen annen lengde, så har du grunnlaget for en god pre-sanitiser - forskjellige tillatte lengder på en streng kan indikere eldre data.

Jeg ville aldri bare ønske å sende søppelet som en ondsinnet bruker kan ha sendt inn gjennom et skjema til de parametriske forberedte uttalelsene, jeg vil alltid gjøre mine egne fornuftskontroller først, og i noen tilfeller kan disse feile på siden av forsiktighet og bare velg å avbryte Database-operasjonen fullstendig.

På den måten blir ikke min DB tilstoppet med usikre utsagn som er gjort sikre - den blir rett og slett ikke tilstoppet, noe som er bedre.

Sikkerhet i lag - sanering og validering bør fortsatt vurderes i enhver situasjon FØR du bruker forberedte uttalelser.

I tillegg så langt jeg kan lese inn i det offisielle dokumentet
==============================================

"Escaping og SQL-injeksjon

Bundne variabler sendes til serveren separat fra spørringen og kan derfor ikke forstyrre den. Serveren bruker disse verdiene direkte ved utføringspunktet, etter at setningsmalen er analysert. Bundne parametere trenger ikke å escapes, da de aldri erstattes direkte i spørringsstrengen"

Det antyder for meg at fare unngås i det indre ved alternativ håndtering ikke ved ugyldiggjøring.

Dette betyr at et stort prosjekt med ufullstendig konvertering til forberedte uttalelser, eldre kode i forskjellige deler av en organisasjon eller servere som snakker med hverandre, kan alle videreformidle de dårlige nyhetene fra et immunsted eller en situasjon til en som ikke er immun.

Så lenge desinfiseringen er kompetent utført uten å pådra meg ytterligere risiko, vil jeg personlig holde meg til visse lag med desinfisering og deretter ringe de forberedte uttalelsene.


Først litt om hvorfor disse skråstrekene er nødvendig generelt.
Hvis vi erstatter noen data i en spørring, må de plasseres i anførselstegn for å skille disse dataene fra SQL-kommandoer.
For eksempel hvis du skriver
VELG * FRA tabell HVOR navn = Regning
da vil databasen bestemme at Bill er navnet på et annet felt, vil ikke finne det, og vil gi en feilmelding. Derfor må de erstattede dataene (i dette tilfellet navnet Bill) omsluttes av anførselstegn - da vil databasen betrakte det som en streng, hvis verdi må tilordnes navnefeltet:
VELG * FRA tabell HVOR navn = "Bill"
Imidlertid kan sitater også vises i selve dataene. f.eks.
VELG * FRA tabell HVOR navn = "D"Artagnan"
Her vil databasen bestemme at "D" er data, og Artagnan er en kommando den ikke kjenner, og vil også gi en feil. Derfor er det nødvendig å spore alle dataene for å forklare databasen at anførselstegnene (og noen andre spesialtegn) som finnes i dem refererer til dataene.
Som et resultat vil vi motta en korrekt forespørsel som ikke vil forårsake feil:
VELG * FRA tabell WHERE navn = "D\"Artagnan"

Dermed fant vi ut at når du erstatter strengdata i en spørring, bør to regler følges:
- alle innsatte strengdata må være omgitt av anførselstegn (enkelt eller dobbel, men enkelt er mer praktisk og oftere brukt).
- Spesialtegn må unngås med skråstreker.

Det bør bemerkes spesielt: lagt til skråstreker går IKKE inn i databasen. De er kun nødvendige i forespørselen. Når du treffer basen, forkastes skråstreker. Følgelig er en vanlig feil å bruke skråstreker når du henter data fra databasen.

Alt ovenfor gjelder strengdata og datoer. Tall kan settes inn uten etterfølgende eller omringe dem med anførselstegn. Hvis du gjør dette da NØDVENDIG! tving dataene til ønsket type før du setter dem inn i spørringen, for eksempel:
$id = intervall ($id);
Men for enkelhets skyld (og pålitelighet) kan du jobbe med tall som med strenger (siden mysql fortsatt konverterer dem til ønsket type). Følgelig vil vi spore alle data som er satt inn i forespørselen og omslutte dem i anførselstegn.

Det er også en regel til - valgfri, men den bør følges for å unngå feil:
Navnene på felt og tabeller skal være omsluttet av enkle anførselstegn - "`" (tasten med dette symbolet er plassert på et standardtastatur til venstre for "1"-tasten) Tross alt kan feltnavnet falle sammen med mysql nøkkelord, men hvis vi bruker et tilbakesitat, vil MySQL forstå at alt er riktig:
VELG * FRA `tabell` HVOR `dato` = "2006-04-04"
Du bør skille mellom disse anførselstegnene og ikke forveksle det ene med det andre. Du bør også huske at backticks ikke unngås av skråstreker.

Så vi har lært hvordan du kan erstatte data på riktig måte i en forespørsel.
MEN! Dynamisk spørringskonstruksjon er ikke begrenset til dataerstatning. Ofte må vi erstatte SQL-kommandoer og feltnavn i en spørring. Og her går vi videre til temaet sikkerhet:

SQL Injection er en metode for hackerangrep når dataene som overføres til et skript modifiseres på en slik måte at spørringen som genereres i dette skriptet begynner å utføre noe helt annet enn det det var ment for.
Reglene for beskyttelse mot slike angrep kan deles inn i to punkter:
1. Arbeide med data.
2. Arbeide med spørringskontroller.

Vi diskuterte det første punktet i detalj ovenfor. Det kan sies at det faktisk ikke er et forsvar. Overholdelse av reglene for å legge til data til en spørring er først og fremst diktert av kravene til SQL SYNTAX. Og som en bieffekt har vi også beskyttelse mot hacking.

Det andre punktet er mye vanskeligere, siden det ikke er noen enkelt universell regel for data - en backtick vil ikke beskytte feltnavnet fra å bli endret av en hacker. Det er ikke mulig å bruke anførselstegn for å beskytte tabellnavn, SQL-setninger, LIMIT kommandoparametere og andre setninger.
Derfor er den grunnleggende regelen når du erstatter kontrollelementer i en spørring:
Hvis du må sette inn SQL-setninger eller navn på felt, databaser, tabeller dynamisk i en spørring, bør du under ingen omstendigheter sette dem direkte inn i spørringen.
Alle alternativer for slike tillegg må skrives på FORHÅND i skriptet ditt og velges basert på hva brukeren har skrevet inn.
Hvis du for eksempel trenger å sende et feltnavn til bestillingen av operatøren, bør du under ingen omstendigheter erstatte det direkte. Vi må sjekke det først. Lag for eksempel en matrise med gyldige verdier, og bytt den inn i forespørselen bare hvis den beståtte parameteren er til stede i denne matrisen:
$orders =array("navn" , "pris" , "antall" );
$key = array_search($_GET["sort"], $orders));
$orderby = $orders [ $key ];
$query = "VELG * FRA `tabell` BESTILL ETTER$ordreby " ;

Vi søker i rekken av forhåndsbeskrevne alternativer for ordet som er skrevet inn av brukeren, og hvis vi finner det, velger vi det tilsvarende elementet i matrisen. Hvis ingen samsvar blir funnet, vil det første elementet i matrisen bli valgt.
Det som erstattes med forespørselen er altså ikke det brukeren skrev inn, men det som ble skrevet i skriptet vårt.
Det samme må gjøres i alle andre tilfeller.
For eksempel, hvis WHERE-leddet genereres dynamisk:
if (!empty($_GET [ "pris" ])) $hvor .= "price="" . mysql_real_escape_string ($_GET ["pris"]). """ ;
$query = "VELG * FRA `tabell` WHERE $hvor " ;

Det er vanskelig for meg å forestille meg et tilfelle der et tabellnavn kan settes inn i en spørring dynamisk, men hvis dette skjer, må navnet også settes inn bare fra et sett forhåndsdefinert i skriptet.
Parametrene til LIMIT-operatoren skal tvinges til en heltallstype ved å bruke aritmetiske operasjoner eller intervall()-funksjonen.
Tro ikke at eksemplene som er oppført her uttømmer alle alternativer for dynamisk spørringskonstruksjon. Du trenger bare å forstå prinsippet og bruke det i alle slike tilfeller.

På grunn av arbeidet mitt må jeg utføre sikkerhetsrevisjoner av kildekoden til nettapplikasjoner.
Mange nettapplikasjoner og mye kode...

Det er ingen hemmelighet at SQL-injeksjonssårbarheter er den vanligste av alle server-side webapplikasjonssårbarheter. Det finnes plattformer og rammeverk der slike ting er nesten helt utelukket, for eksempel ORM osv. Men statistikk forteller oss stadig om den absolutte overvekten av webapplikasjoner med enkle sammenkoblede SQL-spørringer på Internett. I tillegg er det tilfeller hvor ORM er generelt anvendelig Det kan for eksempel ikke når ikke bare parametrene til uttrykk, men også selve spørringslogikken på operatørnivå må avhenge av brukerdata.

Så la oss begynne.

Ubrukelig karakter som rømmer
Finnes i 83 % av PHP-nettapplikasjoner som er sårbare for SQL-injeksjoner
Bruke escape-funksjonen for tegn som f.eks
mysql_escape_string
mysql_real_escape_string
legger til skråstreker
uten anførselstegn. Oftest manifesterer det seg i numeriske parametere (alle typer *_id).
Eksempel
$sql = "VELG bruker FRA brukerliste WHERE userid=".mysql_real_escape_string($_GET["uid"]);

Det ser ut til å være sikker kode, men bare på overflaten. Det vanligste mønsteret av SQL-injeksjoner i PHP i min praksis snek seg inn her. For å angripe denne sårbarheten må en angriper ganske enkelt unngå å bruke " " \x00 \r \n \x1a-tegnene i angrepsvektoren.
For eksempel:
/index.php?uid=-777 UNION VELG passord FRA brukerliste

Søk i kode
Komplisert av språkets semantikk. For et enkelt søk kan du bruke egrep:
egrep -Rin "(velg|oppdater|sett inn|slett|erstatt).*(fra|sett|inn).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

Logikken til søkeuttrykket er som følger: finn alle linjer der det ikke er noen sekvens av anførselstegn ("", "", "", "") til venstre for filtreringsfunksjonene. Metoden er selvfølgelig langt fra 100 %, men det er umulig å kreve et regulært uttrykk for å utføre semantisk analyse.
For å gjøre det enklere å vise informasjon kan du markere funksjonen i farger i konsollen:
egrep -Rin "(velg|oppdater|sett inn|slett|erstatt).*(fra|sett|inn).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

For å beskytte mot dette jokertegnsårbarheten er det best å bruke typecasting.
Dette fungerer alltid raskere og er mer pålitelig enn all slags filtrering og screening.
For eksempelet ovenfor kan oppdateringen være slik:
$sql = "VELG bruker FRA brukerliste WHERE brukerid=".intval($_GET["uid"]);

Dette avslutter det korte essayet. Jeg oppfordrer alle nettutviklere til å prøve å sjekke kildene deres for slike design. Enda bedre, utvide det gitte søkeskriptet for personer.

Så i utgangspunktet gravde jeg dypt inn i områdene MySQL og PHP... spesifikt sikkerhetstiltakene jeg bør ta når jeg arbeider med databasen og skjemainndata. Så langt har jeg funnet følgende å være sterkt anbefalt:

  1. Utarbeidede uttalelser
  2. Bruker _real_escape_string()
  3. IKKE bruk magiske sitater da det forvirrer databaser og ender opp med å gi deg ting som "Du kalte det ikke...".

Alt dette er flott, og jeg holder øye med det. Men jeg lurte på om jeg skulle unnslippe tegn som dollartegn [$], prosenttegn [%] og kanskje andre. Kan spørringen kanskje ha tolket dollartegnet som en PHP-variabel? Hva med LIKE-syntaksen jeg har hørt bruker %-symbolet eller til og med et jokertegn? Utarbeidede uttalelser skulle teknisk sett ta seg av alt dette, men jeg ville bare være på den sikre siden og sørge for at jeg fikk dekket alt ordentlig. I tilfeller der jeg glemmer å bruke forberedte utsagn eller bare neglisjerer dem, håpet jeg at denne andre forsvarslinjen kunne fortelle meg at jeg kunne bli kvitt svimmelheten.

Her er det jeg bruker for å unnslippe:

Funksjon escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($new_data, ENT_NOQUOTES); return $new_data; )

Så er dette riktig? Gjør jeg noe fryktelig galt? Vær oppmerksom på at når jeg returnerer databasedataene, må jeg fjerne skråstrekene før tegnene $,% og _.

Gjør jeg noe fryktelig galt?

Først om forskningen din.

Forberedte uttalelser – den eneste fantastisk ting du fant.

Selv om bruk av mysqli_real_escape_string (forutsatt at du bruker forberedte utsagn) ville være det ubrukelig og skadelig(oppretter et resultat som du selv noterte: "Du ringte isn\t...").

Og Magic Quotes er for lengst fjernet fra språket - dermed egentlig ikke verdt noe.

Så selv de fleste av de opprinnelige premissene dine er helt klart feil.

Nå til spørsmålet ditt.

Kan spørringen kanskje ha tolket dollartegnet som en PHP-variabel?

Hva med LIKE-syntaksen jeg har hørt bruker %-symbolet eller til og med et jokertegn?

Ja, du hørte det riktig. Det nøyaktige formålet med LIKE-operatoren er å utføre et mønstersøk. Å deaktivere disse karakterene i LIKE ville ikke gi den minste mening.

Hver gang du skal bruke LIKE-operatoren, må du bestemme hvilken spesifikk karakter du skal bruke og hvilken du ikke skal tillate. Du kan ikke bruke en engangsløsning. For ikke å nevne at i alle andre mysql-interaksjoner har %-tegnet ingen spesiell betydning.

Utarbeidede uttalelser skal teknisk sett ta seg av alt dette

Forberedte utsagn har ingenting å gjøre med $- eller %-tegn. Forberedte setninger refererer til SQL-injeksjon, men ingen tegn kan forårsake det (du kan ikke kalle "injection" en riktig brukt LIKE-operator, kan du?).

Til slutt, til det verste.

I tilfelle du glemmer å bruke forberedte uttalelser eller bare unnlater å følge dem,

ingenting vil redde deg.

Og den minste hjelpen ville være fra funksjonen du utviklet.

Oppsummer.

  1. Bli kvitt denne funksjonen.
  2. Bruk fyllstoffer * for å representere hver enkelt variabel i spørringen.
  3. Escape % og _ tegn i inndata bare hvis de skal brukes i LIKE-operatoren og du ikke vil at de skal tolkes.
  4. Bruk htmlspecialchars() for utdata, ikke mysql-inndata.

*les forberedte uttalelser hvis dette begrepet ikke er kjent for deg.

Du trenger ikke å unngå dollartegnet. MySQL ser ikke på denne karakteren spesifikt, og PHP gjenkjenner den bare i kildekoden, ikke i strengverdier (med mindre du kaller eval på strengen, men det er en helt annen orm av ormer).

Du trenger bare å unnslippe % og _ hvis du brukte brukerinndata som LIKE-argumentet og du ikke vil at brukeren skal kunne bruke jokertegn. Dette kan skje hvis du behandler et søkeskjema. Du trenger ikke bruke den når du lagrer i en database.

Du trenger ikke bruke htmlspecialchars når du får tilgang til databasen. Dette bør kun brukes når data vises til brukeren på en HTML-side for å forhindre XSS-injeksjon.

Avhengig av hvilke data og hva de brukes til.

Hvis du finner ut at PHPs standard ut-av-boksen-utsagn er for store og komplekse, foreslår jeg at du tar en titt på noen av klassene som er tilgjengelige på github for å gi deg en ide om forenklede spørringer.

Et eksempel på å sette inn spørringer med denne klassen

$data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("hemmelig passord+salt")), // passord = SHA1("hemmelig passord+salt") "createdAt" => $db->nå(), // createdAt = NOW() " expires" => $db->now("+1Y") // expires = NOW() + intervall 1 år // Støttede intervaller [s]sekund, [m]inute, [h]time, [d]day, [Måned år); $id = $db->insert("brukere", $data); if ($id) echo "bruker ble opprettet. Id=" . $id; else echo "insert failed: " . $db->getLastError();