Her er de 25 kodefejl du absolut skal undgå

34 kommentarer.  Hop til debatten
Cross-site scripting og buffer overflow er med helt i toppen af en ny liste over de 25 mest udbredte programmeringsfejl. Dansk sikkerhedskonsulent opfordrer til at droppe 'farlige' C for at undgå buffer overflows.
18. februar 2010 kl. 08:11
errorÆldre end 30 dage

På en ny liste over de 25 mest 'populære' programmeringsfejl og deres tilhørende sårbarheder er det tre gamle kendinge, der huserer i toppen.

Cross-site scripting, SQL injection og buffer overflow indtager de tre øverste placeringer på listen, som er sammensat af en række store sikkerhedsorganisationer- og virksomheder, blandt andet SANS Institute, McAfee og National Security Agency.

Ifølge en dansk sikkerhedskonsulent er der ingen overraskelser hverken i toppen, eller på resten af listens 25 pladser.

Men derfor er den stadig god nok som 'huskeliste' for udviklere.

Artiklen fortsætter efter annoncen

»Det er de sædvanlige fejl, der optræder på den. Men det er problemer, der i nogle af tilfældene har eksisteret i 20 år, så den slags lister kan være nødvendige for endnu engang at få fortalt programmører om problemerne,« siger it-sikkerhedskonsulent i Armada Hosting, Henrik Kramshøj, til Version2.

Listens nummer ét, Cross-site scripting eller XSS, er et angreb, hvor huller i dynamisk hjemmesidekode ? for eksempel PHP eller ASP ? bruges af hackere til at indlemme små bidder Javascript eller HTML på hjemmesiden.

Det kan for eksempel bruges til at lede den intetanende bruger ind på en hjemmeside med skadelig kode.

»Fejlen består i, at man stoler på input fra en utroværdig kilde. Det skyldes ganske enkelt mangel på inputvalidering fra serverens side, eller at inputvalideringen ikke er konsekvent nok,« siger Henrik Kramshøj.

Artiklen fortsætter efter annoncen

Ifølge sikkerhedskonsulenten kan XSS være svær at bide skeer med, fordi det ikke altid er til at se, hvem der bliver ramt af den type angreb.

Undgå for alt i verden C
Listens nummer tre ? det klassiske buffer overflow ? skyldes, at programmøren kopierer et antal bytes fra ét sted i hukommelsen til et andet uden at sikre, at destinationsbufferen er stor nok.

Dermed skrives der data til et sted i hukommelsen, som ikke er afsat til formålet, hvilket kan medføre, at programmet begynder at opføre sig mystisk.

Derudover kan det udnyttes i hackerangreb.

Buffer overflows er ofte knyttet sammen med programmeringssprogene C og C++, og det fik sidste år Microsoft til at anbefale udviklere at droppe brugen af C-funktionen memcpy(), der netop bruges til at kopiere en klump data fra ét sted i hukommelsen til en anden.

Funktionen er i sig selv ufarlig, hvis den bruges med omhu, men problemet er, at funktionen ikke 'tvinger' programmøren til at tænke over, om destinationsbufferen i hukommelsen er stor nok til at holde den klump data, der kopieres.

Microsoft anbefaler i stedet af bruge 'søsterfunktionen' memcpy_s(), der kræver destinationsbufferens størrelse som argument og dermed forsøger at få programmøren til at overveje mere omhyggeligt, hvordan han sætter sine fødder i hukommelsen.

Ifølge Henrik Kramshøj er det én af de ting, der kan være med til at skære antallet af buffer overflows ned.

»På samme måde har man funktionen strcpy() på Unix-platformen, som der også findes forskellige, bedre afarter af, blandt andet strlcpy(). Ved at bruge nogle funktioner, som ikke er så usikre, får man mere kontrol over tingene,« siger Henrik Kramshøj.

Han anbefaler dog helt at undgå programmeringssproget C, med mindre det er det eneste egnede sprog til opgaven.

»Hvis man kan undgå 'farlige' sprog som C, er det også at foretrække. Grunden til at kalde C farligt er, at meget få mennesker er i stand til at programmere sikkert i sproget,« siger Henrik Kramshøj.

C bruges typisk til maskinnære applikationer, hvor det er vigtigt at kunne styre hukommelsesforbruget stramt.

Hvis C er den eneste udvej, anbefaler Henrik Kramshøj udviklerne at ruste sig med både statiske og dynamiske analyseværktøjer, der kan analysere et C-program for potentielle sårbarheder både før programmet er kompileret, og mens det afvikles.

Henrik Kramshøj anbefaler samtidig at tage et kig på open source-projektet OWASP, der fokuserer på applikationssikkerhed (Se under fanebladet Eksterne links).

Se den komplette liste over de 25 programmeringsfejl herunder:

  1. Failure to Preserve Web Page Structure ('Cross-site Scripting')
  2. Improper Sanitization of Special Elements used in an SQL Command ('SQL Injection')
  3. Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
  4. Cross-Site Request Forgery (CSRF)
  5. Improper Access Control (Authorization)
  6. Reliance on Untrusted Inputs in a Security Decision
  7. Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  8. Unrestricted Upload of File with Dangerous Type
  9. Improper Sanitization of Special Elements used in an OS Command ('OS Command Injection')
  10. Missing Encryption of Sensitive Data
  11. Use of Hard-coded Credentials
  12. Buffer Access with Incorrect Length Value
  13. Improper Control of Filename for Include/Require Statement in PHP Program ('PHP File Inclusion')
  14. Improper Validation of Array Index
  15. Improper Check for Unusual or Exceptional Conditions
  16. Information Exposure Through an Error Message
  17. Integer Overflow or Wraparound
  18. Incorrect Calculation of Buffer Size
  19. Missing Authentication for Critical Function
  20. Download of Code Without Integrity Check
  21. Incorrect Permission Assignment for Critical Resource
  22. Allocation of Resources Without Limits or Throttling
  23. URL Redirection to Untrusted Site ('Open Redirect')
  24. Use of a Broken or Risky Cryptographic Algorithm
  25. Race Condition

(Kilde: http://cwe.mitre.org)

34 kommentarer.  Hop til debatten
Fortsæt din læsning
Debatten
Log ind for at deltage i debatten.
settingsDebatindstillinger
1
18. februar 2010 kl. 09:00

»Hvis man kan undgå ’farlige’ sprog som C, er det også at foretrække. Grunden til at kalde C farligt er, at meget få mennesker er i stand til at programmere sikkert i sproget,«

Man kunne med lige så stor ret sige, at man skal undgå 'farlige udviklere'!. I min ringe erfaring har jeg set uforsigtige udviklere lave katastrofer - sikkerhedsmæssige og andre - i alle sprog inklusive sprog med sikkerhedsbælte (frameworks) såsom Java (J2EE), VB, C# (.NET) m.m. Jeg medgiver, at det er lidt sværere end i C. Men den beslutsomme udvikler kommer altid igennem alligevel - framework eller ej ;)

2
18. februar 2010 kl. 09:23

Selvfølgelig kommer den beslutsomme udvikler altid igennem. Men hvorfor skal vi se de samme ret basale standard-strukture blive opfundet igen og igen og igen? Hvis vi nu en gang for alle fik et veldesignet og sikkert sæt API'er for virkelig grundlæggende ting som streng-håndtering, lister og træer så ville mange af de sædvanlige farlige faldgruber blive fjernet uden at fjerne fleksibiliteten og styrken for dem der har brug for char* og generelle pointere.

Problemet er selvfølgelig at få tampet folk til at bruge disse API'er. Det er ikke lang tid siden jeg så et sikkerhedsproblem der skyldtes gets() - samme problematik der lade internettet ned i 1988.

(Og så selvfølgelig ikke sætte idioter til at kode i C)

5
18. februar 2010 kl. 09:54

Problemet er selvfølgelig at få tampet folk til at bruge disse API'er. Det er ikke lang tid siden jeg så et sikkerhedsproblem der skyldtes gets() - samme problematik der lade internettet ned i 1988.

Et klassisk problem er at mange foretrækker at skrive tingene selv, fremfor at betro sig til standard biblioteker - enten fordi de bare ikke vil, eller også fordi de ikke kender bibliotekerne.

Begge dele er til stor gene/skade, da de introducerer muligheder for problemer der var løst for mange år siden, både m.h.t. fejl og performance.

/Kim

3
18. februar 2010 kl. 09:31

Måske er en del af løsningen, at se på teamsammensætningen - vi har alle sammen været begyndere. 'Gammeldags' dyder som peer-review af koden hjælper også tit. Der findes flere biblioteker, der tilbyder en sikrere strenghåndtering m.m., men der er intet i C, der forhindrer, at man benytter bl.a. 'a pointer is a pointer is an int' til at lave lidt hurtig kode her og der. Så vi er tilbage ved kvalitetssans, vejledning og kontrol.

6
18. februar 2010 kl. 09:56

Hvis man som udvikler ved hvad faldgrupperne er OG holder fokus på disse når man arbejder, så undgår man kendte design/kodefejl.

Det handler om at have det på rygraden. Hvis man er C programmør, så skal bufferoverflow problemet side på rygraden. Når man f.eks. bruge memcpy(), så er der en lille klokke der skal ringe.

Årsagen til fejlene opstår igen og igen er helt banal. Der er ikke fokus på konsekvenserne. Problemetikken eller konsekvenserne er enten ikke kendt eller også er de blevet klassificeret som ligegyldige af udvikleren.

4
18. februar 2010 kl. 09:51

[i]3. Buffer Copy without Checking Size of Input 12. Buffer Access with Incorrect Length Value 18. Incorrect Calculation of Buffer Size[/i]

Alle disse vedrører buffere, hvor størrelsen er fast, og hvor man ikke verificerer, om adgang til bufferen går ud over dennes størrelse.

[i]14. Improper Validation of Array Index[/i]

Er lidt i samme kategori, og

[i]17. Integer Overflow or Wraparound[/i]

er også beslægtet -- man overskrider (uden check) en grænse for en ressource.

Jeg er enig i, at brug af C er en stor del af problemet. Man kan selvfølgelig undgå problemerne med omhyggelig kodning, men, som Peter siger, hvorfor skal man spilde sine programmørers tid med den slags?

Det er trivielt at bygge den slags check ind i oversætteren, så der automatisk sættes køretidsverificering ind i den genererede kode. Det er endda forholdsvist nemt for oversætteren at finde 99% af de tilfælde, hvor checket er overflødigt og dermed lade være med at generere kode for det.

Et alternativ er kun at bruge datastrukturer, der udvides efter behov: Hvis man går ud over de nuværende grænser, udvides automatisk til en større grænse. Det giver dog mulighed for denne fejl:

[i]22. Allocation of Resources Without Limits or Throttling[/i]

Men det er et mindre problem: Det kan overbelaste en server, og dermed give anledning til DoS angreb, men det kan ikke alene være årsag til egentlige sikkerhedshuller.

13
Indsendt af Anonym (ikke efterprøvet) den tor, 02/18/2010 - 12:06

Det er trivielt at bygge den slags check ind i oversætteren, så der automatisk sættes køretidsverificering ind i den genererede kode. Det er endda forholdsvist nemt for oversætteren at finde 99% af de tilfælde, hvor checket er overflødigt og dermed lade være med at generere kode for det.

Ja og der findes jo heldigvis programmeringssprog, hvor sådanne checks udføres run-time. Der findes også programmeringssprog, hvor det gøres under oversættelsestidspunktet.

7
18. februar 2010 kl. 10:04

Disse fejl skyldes uden undtagelse et idiotisk design af API'en til SQL: Man koder hele forespørgslen som tekst, så SQL syntaksen ikke bliver adskilt fra parametre såsom søgestrenge. Derfor kan man indsætte SQL syntaks i sine søgestrenge og få dem udført på serveren.

Der bør laves en skarp adskillelse af SQL syntaks (programmørspecificeret) og parametre (brugerspecificeret), så de ikke blandes. Det kan gøres på flere måder:

  1. Samme ide som i printf(): Indsæt i SQL syntaksen typeannoterede markører for pladser til brugerdata, og overfør brugerdata som separate parametre. Dermed kan brugerdata ikke fortolkes som SQL syntaks. Der er dog stadig risiko for, at en doven programmør bygger brugerdata ind i SQL syntaksen.

  2. Opbyg forespørgslen som en datastruktur, hvor syntaks er askilt fra parametre, sådan at der vil komme typefejl, hvis man forsøger at tolke parametre som syntaks. Det gør det dog lidt mere omstændeligt at programmere sine forespørgsler, men hvis sproget ellers har ordentlig understøttelse for datastrukturer, er det ikke så slemt.

  3. Brug Linq-lignende udvidet syntaks i programmeringssproget. Det kræver ændring af sproget, og er dermed uden for rækkevidde af en normal bibliotekskoder. Men ellers er det en god løsning.

14
Indsendt af Anonym (ikke efterprøvet) den tor, 02/18/2010 - 12:10

</p>
<ol>
<li>Samme ide som i printf(): Indsæt i SQL syntaksen typeannoterede markører for pladser til brugerdata, og overfør brugerdata som separate parametre.

Kan man ikke forestille sig at det udnyttes til en snedig form for SQL injection?
15
18. februar 2010 kl. 12:58

[quote]1. Samme ide som i printf(): Indsæt i SQL syntaksen typeannoterede markører for pladser til brugerdata, og overfør brugerdata som separate parametre.

Kan man ikke forestille sig at det udnyttes til en snedig form for SQL injection?[/quote]

Ikke hvis biblioteket er skrevet fornuftigt. Så bliver den tegnfølge, der beskriver SQL-syntaksen, parset uden, at der skeles til parametrene udover, at en parameter indsættes, når der i SQL-syntaksen er et parameterfelt. Og denne parameter bliver [i]ikke[/i] parset som SQL-syntaks. Endvidere bør SQL-parseren verificere, at parameteren har den type, som er angivet i parameterfeltet. Ideelt set bør SQL-biblioteket kræve, at samtlige parametre angives separat fra SQL-syntaksen (selv om det er faste parametre, som ikke kan ændres af brugeren), da man dermed undgår, at en doven programmør bygger en tegnfølge, der indeholder både syntaks og brugerdefinerede parametre i stedet for at holde dem adskilt.

Men biblioteksfunktionerne skal selvfølgelig designes og skrives af kompetente programmører -- hvilket øjensynlig ikke har været tilfældet med de nuværende.

17
Indsendt af Anonym (ikke efterprøvet) den tor, 02/18/2010 - 13:42

Endvidere bør SQL-parseren verificere, at parameteren har den type, som er angivet i parameterfeltet

OK det var netop dette scenario, jeg havde i tankerne. Ellers kunne man vel foretage et DOS angreb, ved at angive en parameter, hvis representation ikke var en gyldig representation af en værdi af den definerede type.

8
18. februar 2010 kl. 10:12

Der bør laves en skarp adskillelse af SQL syntaks (programmørspecificeret) og parametre (brugerspecificeret), så de ikke blandes.

Problemet er blot at de steder hvor SQL injection sker er mest via typefrie programmeringssprog såsom PHP & ASP. Næsten alle programmeringssprog har mulighed for at lave såkaldte "prepared statements". Her kommer så yderligere et problem for specielt PHP, da man har en configurations mulighed for at slå magic quotes til, hvilket kan give yderligere sjove problemet.

9
18. februar 2010 kl. 10:27

Hvis man implementerer prepared statements korrekt eller bruger mysql_real_escape_string() i PHP korrekt så opstår der ikke noget problem i forhold til SQL Injection.

Som regel er det faktisk ret simpelt ligesom med "encoding" af HTML-tegn hvor udvikleren bruger / implementerer htmlentities() helt forkert.

Det handler kort sagt om "Best Coding Practice" og det kan sagtens lade sig gøre. Hvis man ikke stoler på htmlentities tager det ikke mere end 5-10 minutter at lave en funktion selv som gør lige præcis det man gerne vil have den til at gøre, f.eks. fjerne html-tegn eller bytte dem ud med deres "entities" (eller noget tredje).

Et problem som en del udviklere overser er også hvis "register_globals" er slået til i PHP.ini hvilket gør at der kan opstå yderligere sikkerhedsproblemer ved udefinerede variabler som normalt er defineret "on-the-fly" hvilket efter min mening ikke bør bruges til andet end test eller udvikling men ikke et færdigt produkt.

18
18. februar 2010 kl. 15:09

Hvis man implementerer prepared statements korrekt eller bruger mysql_real_escape_string() i PHP korrekt så opstår der ikke noget problem i forhold til SQL Injection.

At skulle foretage sig noget explicit for at gøre sine argumenter sikre (såsom mysql_real_escape_string) er en uheldig måde at implementere sikkerhed på.

Det kan godt være at man husker at gøre det for alle sine argumenter i de første 2000 SQL-kald man laver, men en eller anden dag har man kun sovet 2 timer og forsøger desperat at holde sig kørende på 10. kop espresso og så svipser den måske, uagtet hvor stor en kodeguru man tror man er ;-) Og så har man balladen...

Sikkerhed bør implementeres så det er implicit (om man så skal skrive sit eget API for at opnå det). Men det betyder ikke at udviklere skal pakkes ind i vat, potentielt farlige handlinger skal blot kræve at man foretager sig noget explicit for at få lov til at udføre dem, hvilket gør udvikleren opmærksom på at der kræves ekstra sikkerhedsmæssig opmærksomhed. Altså den omvendte model.

12
18. februar 2010 kl. 12:00

Hvis man implementerer prepared statements korrekt eller bruger mysql_real_escape_string() i PHP korrekt så opstår der ikke noget problem i forhold til SQL Injection.

Det er nok et spørgsmål om målrettet at gå efter at få udryddet tutorials på nettet, der giver eksempler som

$sql="select * from tabel where id=".$_GET['id'];

Jeg forsøgte i en periode at poste prepared statements-udgaver af de usikre sql-kald der blev præsenteret i dk.edb.internet.webdesign.serverside-grupperne, men den opdragelsesmæssige effekt slog desværre ikke igennem...

Leif

10
18. februar 2010 kl. 10:57

Hvis man implementerer prepared statements korrekt eller bruger mysql_real_escape_string() i PHP korrekt så opstår der ikke noget problem i forhold til SQL Injection.

Her er det så at et af problemerne med PHP viser sig - nemlig de DB specifikke implementeringer! Ville være rart om PHP fra starten havde kigget let mere på Perl's DBI modul.

Et problem som en del udviklere overser er også hvis "register_globals" er slået til i PHP.ini hvilket gør at der kan opstå yderligere sikkerhedsproblemer ved udefinerede variabler som normalt er defineret "on-the-fly" hvilket efter min mening ikke bør bruges til andet end test eller udvikling men ikke et færdigt produkt.

register_globals bør aldrig under nogen omstændigheder være slået til, ingen undskyldninger der! Tillader man klyt i en fase, så kan du være 99% sikker på at det også vil være at finde i produktions koden, da man kan glemme at fjerne det.

11
18. februar 2010 kl. 11:20

@Kim Jensen: Du har ret I at Database implementeringen i PHP sagtens kunne have været bedre, men den virker da.

Angående register_globals, så ved jeg godt at man aldrig bør slå den indstilling til, selvom der altså stadigvæk er hosting firmaer der gør det og udviklere som bruger den funktion til at sætte diverse variabler.

Derfor kan man kun konkludere at hvis man absolut skal bruge den funktion, så bør man sikre input der eventuelt kan komme fra register_globals er saniteret (renset) korrekt.

16
18. februar 2010 kl. 13:24

Du har ret I at Database implementeringen i PHP sagtens kunne have været bedre, men den virker da.

Ja, den fungerer - men det var da sågu den mest tåbelige konstruktion man kunne tænke sig. PHP udviklerne lavede en mapping af hver databases C interface, hvilket betød at funktionaliter og konventioner varierede afhængig af databasen.

Den første udgave af PHP var dybt fokuseret på Perl, så hvorfor de ikke snuppede en af de mest elegante dele af Perl, DBI, og tilføjede denne funktionalitet var mig en gåde.

Derfor kan man kun konkludere at hvis man absolut skal bruge den funktion, så bør man sikre input der eventuelt kan komme fra register_globals er saniteret (renset) korrekt.

Problemet med register_globals er at du mister al kontrol over variabel deklarationen. Dette betyder at ikke engang kan stole på dine egne interne variable, men er tvunget til at validare alt. register_globals er simpelthen så farlig at enhver der overhovedet overvejer at anvende den bør bankes gul og blå med en våd Søndags berlinger - indtil de har fattet dybden af deres egen dumhed.