7
Tags:
.net
Skrevet af
Bruger #293
@ 21.03.2006
Jeg vil i den her "Best Practices" serie af artikler fokusere på et par grundlæggende best practices omhandlende brug af ADO.NET.
Artiklerne er affødt af at mange tilsyneladende ikke er bekendt med de forskellige faldgruber der kan opstå når man laver databasetilgang via ADO.NET og jeg vil her forsøge at rette op på nogle af disse. Vi starter i denne artikel blødt ud med at se på hvordan man overkommer et af de mest gængse sikkerhedsproblemer når man arbejder med databasetilgang.
SQL Injections
Enemy #1 og ikke uden grund. Ikke nok med at man alt for ofte ser semi-professionelle sites der overhovedet ikke beskæftiger sig med dette problem og som konsekvens er hullet som en si, når folk endelig håndterer det så er det somregel med en løsning der går ud på at replace tegnet single-qoutes i den ene eller anden udgave.
Det er som udgangspunkt også effektivt nok, det største problem i den forbindelse er at
man kan glemme at replace en string inden den indsættes i SQL'en. Kan det lade sig gøre at glemme noget sådant, så er det blot et spørgsmål om tid før det rent faktisk sker!
Parameters
Løsningen er egentlig ganske enkel. Istedetfor at indsætte værdierne direkte i SQL'en, anvender man istedet parameters til formålet.
Det har minimum 2 fordele:
- Parameters beskytter automatisk mod SQL Injections
- Parameters gør SQL'en mere overskuelig
Så istedetfor den "gængse" løsning:
string sql = "UPDATE User SET Name = '" + GetValidText(NameTextBox.Text) + "', Username = '" + GetValidText(UsernameTextBox.Text) + "' WHERE ID = " + UserID;
Anvendes istedet parameters, som mange af jer formentlig kender fra Stored Procedures.
Disse kan dog også anvendes til ganske almindelige SQL kald og det er det vi her vil gøre brug af:
string sql = "UPDATE User SET Name = @name, Username = @username WHERE ID = @id";
Man sætter så dataene efterfølgende via Parameter collectionen der udbydes af IDbCommand objektet, i dette eksempel benytter jeg den specialiserede udgave til SQL Server:
string sql = "UPDATE User SET Name = @name, Username = @username WHERE ID = @id";
using (SqlConnection conn = new SqlConnection(connstr)) {
conn.Open();
using (SqlCommand command = new SqlCommand(sql, conn)) {
command.Parameters.Add("@name", SqlDbType.VarChar).Value = NameTextBox.Text;
command.Parameters.Add("@username", SqlDbType.VarChar).Value = UsernameTextBox.Text;
command.Parameters.Add("@id", SqlDbType.Int).Value = UserID;
command.ExecuteNonQuery();
}
conn.Close();
}
Ulempen som det også fremgår af ovenstående er så at det hele kommer til at fylde lidt mere kode, dette synes jeg dog personligt ikke er et problem når man tager den øgede gennemskuelighed af SQL koden i betragtning og den sidegevinst at man helt slipper for at bekymre sig om SQL Injections.
Best Practice råd: Brug
altid parameters til indsættelse af data i SQL sætninger, medmindre der er en meget god grund til at gøre en undtagelse.
Fodnote: Bemærk at parameters desværre ikke kan benyttes i WHERE IN (...) sætninger og at en replace af single-quote tegnet i dette tilfælde ikke løser problemet. Sørg derfor altid for at være særlig opmærksom på brug af denne konstrukt og lav passende input validering.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (8)
Hvis "parameters" er .NET sidestykket til "prepared statements" i Java, er beskyttelsen mod SQL Injections kun en bi-virkning.
Hovedformålet med at trække parametrene ud, er at man sikrer at typerne er korrekte (ingen "sen" database type-cast på streng værdier) og hvis databasen understøtter det, oversættes sql sætningen kun første gang der stødes på den, herefter kan den oversatte (og optimerede) interne database version af sql sætningen med parametre, hentes frem (af SGA'en kalder Oracle vist deres) og parametrene til hvert kald kan blot puttes på plads med et minimum af CPU forbrug på database serveren.
Det er særligt brugbart hvis man skal loope x antal gange med en identisk sql opbygning, men med forskellige værdier.
Her bygger man kun sql sætningen en enkelt gang, og skifter parametre for hvert loop, og hvis databasen understøtter det, tjekkes syntaxen kun første gang og herefter køres der bare løs.
At man binder data typerne i programmet ved compile istedet for ved runtime, i forhold til sin sql sætning, byder naturligvis på den fordel som du nævner at man bliver tvunget til at lave lidt mere orden på sin kode og det bliver nok nemmere at læse.
Ellers en fin introduktion til ideen
Lige et dumt spørgsmål:
Hvordan har du testet WHERE IN med parametre?
... "WHERE ID IN (@id1, @id2, @id3)"
og et parameter sættes for hver værdi?
eller
... "WHERE ID IN (@ids)"
og værdierne samles i din kode og sættes på en gang?
Mener ikke Java's prepared statements har problemer med WHERE IN ... så hvorfor skulle ADO.NET have det?M$ plejer ikke at forværre tingene når de kopierer, ups nej, lader sig inspirere, he he
Meget fin artikel omend den måske er lidt kort (du kunne måske godt have taget endnu et emne ind under denne artikel?).
Men artiklen er da skrevet i et rigtig let forståeligt sprog og det er jo bare herligt
.
Dejlig artikel om parameters, omend lidt kort. Fordelen ved parameters er, som der også bliver beskrevet ovenfor, at der tages højde for objekt konvertering, og faktisk også lokalisering (så vidt jeg husker, eks. datoformat)
#1, #3, #4:
Takker
Jeg tror dog I misforstår artiklen en smule da meningen ikke var at debattere parameters (eller hvad man nu vælger at kalde dem). Det er med vilje at fokus er holdt helt og aldeles på at komme SQL injections til livs uden for meget udenomssnak da det som titlen antyder er ment som tagende udgangspunkt i praktiske problemstillinger og iøvrigt gerne skulle være let læselig for begyndere.
Men det er selvfølgelig helt perfekt at I uddyber Parameters yderligere her i kommentar-delen :-)
Der bliver iøvrigt lidt mere kød på næste artikel
#2: Uden lige at have testet det vil jeg tro at "WHERE ID IN (@id1, @id2, @id3)" virker. I de tilfælde hvor jeg normaltvis har brug for det vil det dog være en noget mere omsonst løsning, så i det her tilfælde tager jeg udgangspunkt i "WHERE ID IN (@ids)". Jeg kan dog godt se at slutkommentaren kan misforståes og ikke burde tage udgangspunkt i min egen situation
(artiklen var oprindeligt postet som et indlæg på min blog, det kan være farligt når man senere konverterer den til en artikel :-))
Kan man også bruge det der i ASP ? Altså ikke .Net ?
Herligt, endnumere omskrivning af min kode. Det er sgu snart nememre at starte helt forfra!
Det er godt at du tager dette emne op. Det sted hvor jeg er i lære, viste min mester mig vigtigheden af at sikre sig af denne svaghed i SQL. Jeg prøvede umiddelbart efter at komme ind på forskellige sider, ved hjælp af denne simple teknik, og det lykkedes på forbavsende mange gange. Jeg prøvede dog kun på "små" hjemmesider, da det ikke var noget jeg havde tænkt mig at drage fordel af, men jeg kunne tænke mig at der var mange der ville, hvis de kunne. Og da det er en meget simpel teknik at mestre, synes jeg ikke at man bør lave kode, hvor SQL-Injections er mulig. Og man kan slet ikke tillade sig, ikke at tage højde for det, hvis det er noget man skal sælge videre.
Du skal være
logget ind for at skrive en kommentar.