Dagbog-bloggen

Derfor skal du have en responsible disclosure policy

Sidste sommer offentliggjorde vi hos Kviknet vores politik omkring offentliggørelse af sikkerhedshuller, som andre finder i vores systemer.

Kort fortalt går den ud på, at vi opfordrer folk til at kontakte os, hvis de finder sikkerhedshuller i vores systemer, og samtidig lover vi, at vi ikke vil forsøge at retsforfølge dem, hvis de optræder ansvarligt.

Det gjorde vi i en erkendelse af, at sikkerhedshuller med garanti findes overalt, også hos os, og at vi hellere vil være på god fod med dem, der finder dem og fortæller os om dem, i stedet for at true med bål og brand, som mange virksomheder, især ældre virksomheder, desværre har for vane.

Vores tilgang har vist sig at være en rigtig god ide.

Vi har modtaget flere henvendelser siden, og især en enkelt henvendelse var af overraskende høj kvalitet. Jeg bringer den her med afsenderens tilladelse:

================================

Greetings,

This report is sent in accordance with the instructions on https://www.kviknet.dk/en/report-an-error

Description

The page https://www.kviknet.dk/custom.js_v2.php is vulnerable to reflected XSS attacks through the GET parameters "partner" and "lang".

Attack scenario

An attacker can create a malicious link containing an arbitrary XSS payload. The payload will execute under the scope of the vulnerable domain in the browser of any victim who visits the link. This could e.g. be used to steal sessions, manipulate page content, tamper with cookies, launch browser-specific exploits or circumvent the web site’s CSRF protection.

Proof of concept

The following link triggers a (harmless) javascript alert, thus serving as a PoC of successfully injected javascript through XSS. This was tested using Mozilla Firefox 58.0 (latest version).

https://www.kviknet.dk/custom.js_v2.php?v=11&lang=en&partner=%3csvg%2fon...

Screenshot

Illustration: Kasper Karlsson

Recommended solution

Make sure to properly encode all GET parameters when echoing it to the page. Also - for this specific page which only returns Javascript, you may want to consider changing the page's Content-Type header to "application/javascript". This will prevent it from being rendered as HTML in a client's browser.

Thank you in advance for your attention to this matter. Please let me know if I can assist you further to resolve this vulnerability!

Best regards

Kasper Karlsson

Omegapoint Göteborg, Sweden

================================

Kasper Karlsson fra Göteborg i Sverige havde fundet en såkaldt cross-site scripting-sårbarhed (XSS vulnerability). I praksis betød den, at en angriber kunne sende et link til sit offer, som i praksis fik offerets browser til at udføre JavaScript, der for browseren så ud som om, det kom fra vores website.

Et sådant angreb kan bruges til manipulation af cookies og i visse tilfælde også at overtage en eksisterende session på vores website.

Selv om risikoen for udnyttelse af en sådan sårbarhed er lille, skulle hullet selvfølgelig lukkes hurtigst muligt. Det viste sig at skyldes en manglende urlencode()-funktion i et PHP-script, der danner en JavaScript-fil, som blandt andet anvendes til integrationen mellem vores website og DAWA's adressedatabase.

Sårbarheden fik os samtidig til at undersøge vores kodebase for andre lignende huller.

Relativt kort tid efter var der gevinst. Det viste sig, at vi anvendte PHP's htmlentities-funktion forkert.

Af historiske årsager (herunder bl.a. kommunikation med TDC's systemer) skal vi kunne håndtere både UTF8-encoded input og ISO-8859-1-encoded input, mens vores output altid skal være UTF-8. Vi udarbejdede derfor en generisk wrapper omkring htmlentities:

static function html_encode($str) {
    // Check existing charset
    if (mb_detect_encoding($str, 'UTF-8', TRUE) === FALSE) {
        return htmlentities($str);
    } else {
        return htmlentities($str, NULL, "UTF-8");
    }
}

Vi havde dog overset en ret vigtig default-indstilling i htmlentities() - af uransagelige årsager har udviklerne bag PHP valgt at htmlentities() som standard ikke encoder single quotes (apostroffer) og double quotes (anførselstegn).

Derved beskytter htmlentities() ikke mod XSS-angreb - forestil dig, at en angriber indtaster følgende tekst i bemærkningsfeltet under bestilling på www.kviknet.dk:

" onload="javascript:alert('Ring til Kviknet og sig, jeres system er blevet hacket!');

Når ordren så lander i vores bestillingssystem og vises i et tekstfelt i en browser, er der fri adgang til skydning i det muntre køkken:

<input type="text" name="comments" value="" onload="javascript:alert('Ring til Kviknet og sig, jeres system er blevet hacket!');" />

Og det ville, for at sige det på godt jysk, være træls.

Løsningen er at tilføje et par flag til htmlentities-funktionen i vores wrapper:

return htmlentities($str, ENT_QUOTES | ENT_HTML401, "UTF-8");

Derved bliver både single quotes og double quotes encoded til deres respektive HTML-ækvivalenter, hvorved browseren renderer dem korrekt:

<input type="text" name="comments" value="&quot; onload=&quot;javascript:alert(&#039;Ring til Kviknet og sig, jeres system er blevet hacket!&#039;);" />

XSS-angrebet bliver på denne måde både synliggjort og forhindret i en og samme proces.

Hvad kan man lære af dette?

Visse udviklere advokerer for, at man skal begynde at sortere i, hvad brugeren må indtaste af værdier. Fx har jeg set eksempler på systemer, hvor man ikke kan anvende apostroffer, når man indtaster data i et felt.

Ud over de åbenlyse problemer med denne tilgang, fx at man ikke kan skrive irske navne som O'Neill eller anvende genitiv på forkortelser og ord, der ender på hvislelyde, afslører det også en manglende tillid til, at man i organisationen evner at håndtere brugerinput på sikker vis.

Denne mistillid er i visse tilfælde helt berettiget, men ikke desto mindre giver en sådan filtrering af brugerinput en masse besvær for brugerne og en falsk følelse af tryghed.

Man kan ikke stole på data, heller ikke selv om man tror, man har filtreret det for skadeligt indhold først - der vil altid være noget, man har glemt.

Derfor må målet være, at man sikrer sine systemer i dybden og altid behandler data, som om det er fyldt med giftig kode, som blot venter på en chance for at blive kørt.

En anden vigtig lære af denne sag er, at det viser, hvor vigtigt det er at tage venligt imod input fra pentesters og friendly hackers.

Ved at offentliggøre en responsible disclosure policy, kan man vise omverdenen, at man er klar til at tage imod konstruktiv kritik.

Og hvis man er heldig, får man endda gode fejlmeldinger af høj kvalitet.

Herfra skal der lyde et tak til Kasper Karlsson for hans bidrag til at højne sikkerheden på internettet.

Relateret indhold

Kommentarer (29)
Baldur Norddahl

Det er en god disclosure politik. Vi bør kopiere den.

Med hensyn til den fundne bug vil jeg slå et slag for en mere moderne måde at udvikle sites. Og det er at lade browseren overtage meget af det arbejde, som tidligere blev udført af serveren. Meget logik udføres i JavaScript og med DOM manipulering, hvilket i stor stil medfører at html entity encoding problematikken ikke er relevant.

Serveren har statiske html filer. Disse filer ændres ikke ud fra brugerinput og de skal ikke behandles af et server sprog som PHP. De skal bare ligge som statiske filer ligesom billeder etc.

Alt dynamisk indhold kommer via et API som leverer JSON data til klienten. Her vil det være en fordel hvis man ikke forfalder til at lave JSON manuelt på serveren. I stedet bruger du et bibliotek, der konverterer et objekt til JSON, uden mulighed for at det kan snydes til at lave falsk JSON.

Fordi der kører et lille program i browseren, så kan mange ting løses direkte og nemmere. Eksempelvis hvis du har trin 1, 2 og 3 i en bestillingsprocess, som førhen kræver at data fra trin 1 sendes til serveren så at den kan generere html til trin 2. I stedet er disse data gemt i lokale variabler og der er måske ikke engang noget kommunikation med serveren, før at sidste trin er gennemført. Der er hellere ikke brug for cookies eller en session id, som overføres med hvert kald til serveren.

Et eksempel: https://gigabit.dk/tjek.html#Snehvidevej 2, 2730 Herlev

Først når du går til betalingssiden bliver der kommunikeret med serveren og det er for at gemme det indtastede inden at brugeren sendes til en tredjepartsside (betalingsgatewayen). Endvidere er tjek.html en statisk fil og adressen bliver tjekket via API kald.

Du kan proppe alle de HTML tags og entities ind i URL'en som du har lyst til. Koden der indsætter adressen på siden, der hvor der står "valgt adresse", gør absolut ingen forsøg på at escape det. Det er ikke nødvendigt. Browseren fortolker ikke tekst der ændres med et funktionskald direkte i DOM træet.

Hvis du ikke kan lide Java_Script, så findes der mange andre sprog som kan oversættes til Java_Script. Og snart også Web Assembly. Selv bruger jeg Scala-JS http://www.lihaoyi.com/hands-on-scala-js/

Helge Svendsen

Jeg et ikke ude på at starte en programmerings sprog borgerkrig.

Men jeg synes der er mange security advice. Mht php? Og mulighed for at lave disse kode fejl. Måske er der mere moderne sprog, der er bedre til we udvikling?

Yoel Caspersen Blogger

Med hensyn til den fundne bug vil jeg slå et slag for en mere moderne måde at udvikle sites. Og det er at lade browseren overtage meget af det arbejde, som tidligere blev udført af serveren.

Jeg er sådan set enig i dine betragtninger. Når man udvikler et sådant website er der dog et par udfordringer, man bør adressere:

  • Brugeren forventer at frem- og tilbageknapperne i browseren virker, især på mobil
  • Der findes klienter, der ikke kører JavaScript (herunder visse webcrawlers, som man gerne vil have besøg af mhb. SEO)
  • Der kan være behov for tracking af, hvor langt i en bestillingsproces en bruger når (primært et issue hvis ens tracking består af parsing af server logs)

Hvis det er lavet godt, kan man dog opnå en ganske god hastighedsforbedring for de fleste brugere. Samtidig har man et API, som fra starten er klar til at man kan udvikle tredjeparts brugerflader.

Hos Kviknet startede vi på mere traditionel maner - og vores første iteration af websitet var heller ikke responsive. Det er dog løst sidenhen, men det kan sagtens ske, at vi koder websitet om, så det blot er en rich client med et API bagved. Der er dog andre projekter, der er højere på ønskelisten lige nu - fx en lab-test af et backbone-netværk baseret på SDN :-)

Yoel Caspersen Blogger

Men jeg synes der er mange security advice. Mht php? Og mulighed for at lave disse kode fejl. Måske er der mere moderne sprog, der er bedre til we udvikling?

Der er mange gode grunde til at undgå PHP.

Det er dog ikke lykkedes mig at finde en god erstatning for PHP endnu - jeg lurer lidt på Python som en mulighed, men der er følgende krav fra min side:

  • Sproget skal have en vis udbredelse, så vi ikke låser os fast på, at vores scripts m.v. kun kan vedligeholdes af en enkelt person
  • Det skal performe og skalere godt (og her er PHP7 desværre rendt fra Python!)
  • Det skal løse de problemer, vi p.t. har med PHP

Gode bud modtages gerne.

Baldur Norddahl

Du skal ikke tage mit website som eksempel på hvor godt det kan gøres - det skal også kodes om den famøse dag hvor jeg har tid :-). Jeg er dog i gang med at fikse sådan noget som back knappen.

HTML5 har tilføjet nogle værktøjer til at styre browser historien, dvs. frem og tilbage knapperne: https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method

Tracking kan teknisk set være ulovligt med de nye EU regler. Må du gemme navn og adressedata på folk der afbryder bestillingsprocessen? Jeg tror det ikke. Men ellers er det nemt nok at lave et tracking API. Jeg må dog sige at jeg aldrig har følt behovet, hvorfor vores side ikke registrerer noget før at det er nødvendigt, dvs. i det øjeblik den kommende kunde sendes videre til QuickPay.

WebCrawlers kan være en udfordring. Men hvis du ser på kildekoden på tjek.html, så indeholder den alt tekst som kan være interessant for en søgemaskine. Et andet alternativ er at lade serveren render siden i disse tilfælde - den behøver ikke være fuldt funktionsdygtig i forhold til søgemaskiner og der findes flere server render der udfører dit javascript på serveren og sender resultatet til klienten. I vores tilfælde er det dog ikke det store problem, da det kun er bestillingsprocessen du ikke kan komme igennem uden javascript.

Klienter der ikke kører javascript og som viser indholdet til ægte mennesker (dvs. lynx) er så lille et marked, at jeg vælger ikke at understøtte det. Jeg vil iøvrigt hævde at der er meget der ikke fungerer uden javascript. Eksempelvis DAWA og dermed vil adressetjekket også fejle, hvis ikke brugeren staver adressen 100% sådan som det offentlige har registreret den.

Baldur Norddahl

Gode bud modtages gerne.

Jeg satser som bekendt på Scala + Scala-JS. For mig er det vigtigt at det er et sprog med statiske typer.

Men hvis Python er din ting, så er det også et godt bud? Der findes flere Python til javascript compilere, så du kan køre python både på server og klient. Eller du kan vælge simple stupid og køre python på server og javascript på klient.

Eller du kan vælge javascript på server og klient, dvs. Node.js. Jeg har aldrig prøvet det, men det synes at være populært og du burde ikke have problemer med at finde folk der har erfaring med javascript. Personligt har jeg det dog med javascript på samme måde som med PHP :-)

Der er imponerende mange sprog der der kan oversættes til java_script: https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-co...

Hans Schou

mange gode grunde til at undgå PHP


Så (mangel på) input validation er PHP's skyld? For det er hvad han skriver.

Ældre læsere husker nok, at Blue Screen Of Death var hvad man mindst så en gang dagligt på Windows 3.0 et al, og det var ikke C's skyld. Det var mangel på input validation.
Dette er ikke nogen Windows-bashing da det også fandtes på Linux. Her blev det ikke kaldt BSOD, men Segmentation Fault.
Input validation løste problemet...

Benny Lyne Amorsen

Input validation går galt før eller siden. Enten er man striks, og så får man de problemer som Yoel nævner med O'Neill, eller også er man for slap, og så slipper noget igennem som man troede var ufarligt.

Problemet er heldigvis løst for SQL, man bruger bare parametre i stedet for at generere SQL ud fra brugerinput. Der er ikke længere nogen grund til at validere input bare fordi det skal igennem en SQL-database.

Desværre er det ikke lige så rosenrødt for HTML. Der er endnu ikke fundet en helt sikkert løsning der kan fortælle en browser "det som kommer nu er ren tekst, hvis det indeholder kode må koden ikke udføres, og alt som vises til brugeren skal holde sig inde i den boks som jeg tildeler".

Man kan så forsøge sig med escaping, f.eks. med htmlentities(). Men det går tit galt alligevel: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Ch...

Baldur Norddahl

Desværre er det ikke lige så rosenrødt for HTML. Der er endnu ikke fundet en helt sikkert løsning der kan fortælle en browser "det som kommer nu er ren tekst, hvis det indeholder kode må koden ikke udføres, og alt som vises til brugeren skal holde sig inde i den boks som jeg tildeler".

Det er der: https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent

Element.innerHTM_L returns the HTM_L as its name indicates. Quite often, in order to retrieve or write text within an element, people use innerHTM_L. However, textContent often has better performance because the text is not parsed as HTM_L. Moreover, using textContent can prevent XSS attacks.

// Set the text content:
document.getElementById("divA").textContent = "This is some text";

Du kan have alt det skidt du vil da textContent ikke bliver fortolket som html.

Yoel Caspersen Blogger

Så (mangel på) input validation er PHP's skyld? For det er hvad han skriver.

Er det PHP's skyld at en bug som den nævnte opstår? Nej, ikke direkte. I sidste ende er det selvfølgelig udviklerens ansvar at teste, at koden opfører sig som forventet.

Men hvorfor er default for htmlentities(), at quotes ikke encodes, medmindre man eksplicit sætter de flag, der får det til at ske?

Mit gæt er, at man først udviklede htmlentities() fordi man gerne ville håndtere tekst, der indeholder < og >. Senere opdagede man, at der også var et behov for at encode double quotes, og senere igen fandt man ud af, at det også kunne være fint at kunne encode single quotes, fordi nogle udviklere skriver html med single quotes omkring parameterværdier. Det kunne man dog have fundet ud af ved at læse standarden fra W3C fra starten af.

Med andre ord, funktionen ser ud til at være udviklet ad flere omgange uden en egentlig styring af, hvad det var, man ville opnå. Og det er sådan ca. sådan hele PHP er skruet sammen - det er flikket sammen af tilfældige knopskydninger og uhensigtsmæssigheder.

Og det er præcis det, der gør PHP til et sub-optimalt valg til rigtig mange ting. Til gengæld er det vildt og voldsomt udbredt, og det er derfor nemt at finde folk, der kan vedligeholde PHP-kode, og performance-mæssigt er det efterhånden også ret godt med. Men laveste fællesnævner er ikke nødvendigvis den bedste.

I øvrigt svarer input validation til en perimeter-firewall i et netværk: Har du først fået noget snavs indenfor, er der frit spil. Det er på tide, vi får gjort op med den tanke, at sikkerhed er noget, der skal ligge i periferien, og kun der.

På et netværk bør der være firewall på samtlige devices, og i et program bør man aldrig stole på data, som ikke har en garanteret chain of custody - har man skrevet data til en database, og henter det på et senere tidspunkt, er der ingen garanti for, at data ikke er blevet ændret i mellemtiden. Derfor bør man IMHO altid betragte data med skepsis, og sikkerheden skal derfor indbygges i samtlige funktioner, der behandler data.

Hvilken input validation vil du anvende på et navn på en person, du gerne vil have som kunde? Og kan du garantere, at den input validation sikrer, at der ikke kan slippe potentielt skadelig kode igennem?

Junior Ronnie

Dejligt at se gode eksempler på php manglende evner.

Bruger selv en hel del php og synes at den har mange gode sider men elsker når der kommer eksempler fra den virkelig verden om dens svagheder.

Htmlentities er en grundlæggende god funktion men som du selv nævner er den bare ikke god nok. Når man bruger den regner man med at den fjerne alt og man så kan fjerne nogle af de ting den konventere.
Dens type håndtering er også dybt forældet.

Jeg kunne godt tænke mig at man udviklede et nyt sprog som håndtere tingene helt anderledes. Der ud over synes jeg at hele webteknologien er forskruet. JavaScript manglende understøttelse af TCP er nok den del der irritere mig allermest :'(

Carsten Gehling

Gode bud modtages gerne.

Jeg har været hele paletten rundt... PHP i 7 år. Python i 2-3 år, Ruby on Rails i 6 år. De sidste 3 år har været på .NET platformen med c#. Og nu er jeg virkelig blevet glad og tilfreds med dette typestærke sprog, jeg får så meget foræret.

Så min anbefaling til dig vil være: C#, .NET Core 2, WebAPI. .NET Core er netop bygget til at køre cross-platform og har høj performance.

Hans Schou

Hvilken input validation vil du anvende på et navn på en person, du gerne vil have som kunde?


Jeg kender en Möller der havde rigeligt problemer, men en helse shop der får en kunde, der har været hos en nepalesisk numerolog, og er kommet til at hedde जननी Jensen, skal også kunne håndteres. (v2 klarede i øvrigt fint जननी)

Hvad angår htmlentities medgår jeg gerne at det er en skrammel library funktion. Sådan er det med libraries; der er nogle man skal holde sig fra, og det gælder alle sprog. Hvis man fx skal bruge Jason sammen med Java, så er der rigtigt mange at vælge imellem. Nogle af dem er ikke ret gode, men det er sådan set ikke Java's skyld.

Konklusionen er, at når man har funktion der virker dårligt, så retter man den, finder en anden eller skriver sin egen. Det gælder for alle sprog.

Yoel Caspersen Blogger

Til gengæld kan den ikke tåle at du skriver ordet javascript for mange gange i et indlæg. Det tyder på noget rigtigt farligt kode som forsøger gætte om et indlæg indeholder noget skadeligt.

Det ser sådan ud. Jeg måtte sidste sommer have fat i Version2's support for at få whitelisted min konto, så jeg kunne få lov at indsætte kode-eksempler i et blogindlæg om vores looking glass. Den flippede helt ud, da jeg ville vise, hvordan man kan eksekvere et systemkald fra PHP ;-)

Carsten Gehling

Hvilken editor bruger du til det?

Visual Studio, og selve kodearbejdet foregår på Windows. Fordi IDE'et giver så ufattelig mange fordele med intellisense, refactoring, tdd, osv. Men fordi jeg er die-hard VIM'er, har jeg suppleret med en extension kaldet VsVim. Så er jeg i lykke-land. :-)

Men al staging og production ruller på Ubuntu servere. Det samme med build serveren.

Ivo Santos

Meget logik udføres i JavaScript og med DOM manipulering, hvilket i stor stil medfører at html entity encoding problematikken ikke er relevant.

Hvis nogen tror at alle problemer kan løses ved at skifte over til 'javascript' og 'JSON' så er det helt hen i vejret da både 'Meltdown' og 'spectre' netop kræver at man har javascript aktiveret i ens browser, og den bedste måde at sikre sig mod de fleste typer at ondsindet kode på websider er netop ved at slå javascript fra, så derfor er 'javascript' og 'JSON' netop ikke altid svaret på alle problemer.

Node.js:
En anden ting jeg kom til at tænke på er at det teoretisk er muligt at udnytte både 'Meltdown' og 'spectre' på websider som kører med 'node.js' så selv der er javascript heller ikke altid den rigtige løsning.

CMS (Content Management System):
En anden stor sikkerhedsbrist er de såkaldte CMS'er så som f.eks. Wordpress og Drupal samt plug-ins til CMS'er som sådan set også er en trussel i sig selv, og det er noget som almindelige brugere ikke kan gøre ret meget ved.

Derfor, så er en del af løsningen vel at funktionaliteten på server niveau forbedres således at det er svære for ondsindede personer at bruge en webserver til at angribe uskyldige personer, og ligeledes sikre at CMS'er og lignende er bedre kodet og testet.

På samme måde som det er muligt at blokere for tredjeparts cookies, så savner en mulighed for at man kan blokere for tredjeparts javascript kode, og så muligheden for at kunne slå html5 komponenter så som lyd og video fra på browser niveau, det vil uden tvivl gøre klient browserene mere sikker.

Arne Jørgensen

En anden stor sikkerhedsbrist er de såkaldte CMS'er så som f.eks. Wordpress og Drupal samt plug-ins til CMS'er som sådan set også er en trussel i sig selv, og det er noget som almindelige brugere ikke kan gøre ret meget ved.

Hvorfor det?

Er der noget særligt ved CMS'er der gør at de skulle være mere tilbøjelige til at indeholde fejl end andet software?

Er det fordi du ikke har skrevet koden selv? Men hvor stopper det argument så? Compiler, OS, firmware, hardware?

Baldur Norddahl

da både 'Meltdown' og 'spectre' netop kræver at man har javascript aktiveret i ens browser,

Kan du ikke nævne et eneste tilfælde af nogen der er blevet hacket via Meltdown eller Spectre? Via en browser? De to angreb er meget teoretiske i browser sammenhæng og det er i øvrigt blevet fikset.

Uden JavaScript er vi tilbage i 90'er udgaven af web. Alle populære sider er afhængige af det. Så det er ikke noget folk slår fra i stor stil.

Du kører også med JavaScript slået til. Ellers kunne du ikke skrive indlæg her på v2.

Jonas Schwartz

Personligt vil jeg anbefale dig at proeve at kigge paa:

1) Jeg er meget biased her :D Swift (Vapor framework), jeg er selv en del af teamet bag Vapor. Swift begynder virkelig at tage fart som sprog.
I vores tilfaelde med Vapor, og med vores seneste Vapor 3.0 er det fuldt ud Async, og har native Swift understoettelse for MySQL, PostgreSQL, MongoDB, Redis m.v.
Der er naturligvis ogsaa andre frameworks Kitura(IBM) og Perfect

Jeg ved Apple ikke altid har bedste navn, men de har gaaet all in paa Open source og Linux med Server Side Swift :)

2) Go, begynder ogsaa at vinde meget ind, og flere og flere web frameworks begynder at skyde frem.

Jeg er selv tidligere PHP udvikler, med mange aars erfaring, at skifte til Swift var ikke den store udfordring .. Eneste minus lige nu er at Swift kun koere paa Ubuntu og MacOS (Ingen Windows support pt.)

Log ind eller Opret konto for at kommentere