Hvordan implementeres autentificering korrekt?

Gennem tiden har jeg flere gange implementeret forskellige former for
autentificering. Nogle gange har jeg været bundet af forskellige
protokol-beslutninger og andre gange har jeg været mere frit stillet.

Derfor har jeg også haft mulighed for at samle mig en række ideer om hvordan
autentificering bør gøres rigtigt. De falder i 3 dele: 1) Forarbejdet på
klienten, 2) selve autentificerings-protokollen og 3) implementationen på
serveren.

Forarbejdet på klienten

Forarbejdet på klienten består i at tage brugerens input og omforme det til en
sekvens af bytes der kan bruges som det egentlige kodeord.

Tidligere viste man bare brugeren et password-felt og brugte hvadend brugeren
nu tastede. Hvis man i dag vil bruge kodeord skal man blandt andet forholde
sig til at i Unicode er et 'Å' ikke bare byte-sekvensen 0xC5, men kan skrives
på (mindst) to forskellige måder i Unicode og dernæst kan kodes i både UTF-8, UCS-2
og et par flere måder. Et godt udgangspunkt er
RFC 4013 (SASLprep).

Hvis man er mere eventyrlig kan man erstatte password-feltet med noget mere
visuelt. Eksempelvis man man lade brugeren tegne streger på et 16x16 grid af
ikoner og så omforme dette til en byte-sekvens. Jeg har set en del forslag til
hvodan dette skulle se ud, men ingen der rigtig har overbevist mig. Nogen der
har et rigtig godt eksempel på mere visuelle kodeord brugt i praksis?

Som det sidste skridt bør man vælge at obfuskere brugerens kodeord. Formålet
er første og frememst at beskytte brugere der vælger samme kodeord til
forskellige systemer; men det yder også en vis beskyttelse mod kodeord der kan
gættes via en ordbog. Forskellige muligheder er
PBKDFv2 eller
scrypt. I begge
tilfælde skal man vælge et salt som er specifikt for ens system.

Det er resultatet af det sidste skridt serveren skal kunne sikre sig at
brugeren kender (herefter kaldt kodeordet). Det vil sige at hvis man kender dette resultat, så kan man udgive sig for at være brugeren.

Selve autentificerings-protokollen

Den helt simple protokol er bare at sende hele kodordet til serveren, der så
kan slå op i brugerdatabasen for at tjekke om kodeordet er korrekt. Det
betyder at en angriber der kan lytte med kan opsnappe kodeordet og bruge det
senere. Det kan enten ske fordi man har glemt at bruge SSL, fordi SSL er noget
juks eller fordi brugeren forbinder sig til et forkert eller kompromiteret
server.

Det vi gerne vil opnå er at selvom det skulle lykkes en angriber at lytte med
på selve autentificeringen, så skal angriberen ikke senere kunne udgive sig
for brugeren. Det vil sige at man ikke må kunne gætte selve kodeordet ud fra
kommunikationen mellem klienten og serveren.

For at opnå dette kan man hashe kodeordet sammen med en vilkårlig streng som
serveren vælger og en streng som klienten vælger. Et godt udgangspunkt er at
se på RFC 5802 (SCRAM), men ideen er den
samme som HTTP Digest mode hvis man
anvender MD5-session metoden.

Denne slags challenge/response protokoller kræver dog at serveren har adgang
til brugerens kodeord og ikke bare en hash af kodeordet. Det stiller nogle
højere krav til opbevaringen af kodeord på serveren.

Implementationen på serveren

Oftest vil angreb være rettet mod at kompromitere selve serveren. Det mest
indlysende er at angriberen simpelthen kanhente en liste af brugernavne med
tilhørende kodeord i en eller anden form. Selv hvis kodeordene er salted og
hashet efter bedste evne giver det angriberen mulighed for kunne gætte kodeord
i ro og mag.

Et godt design vil derfor minimere mængden af kode der har adgang til hele
listen af kodeord. Derfor mener jeg man bør implementere autentificering som
en selvstændig service, der kan beskyttes uafhængigt af resten af
applikationen. Helt enkelt kan det gøres med en database hvor applikationen
ikke kan lave en 'SELECT * FROM passwords' men kun kan validere enkelte
passwords med en stored procedure 'SELECT authentificate(username,salt,hash)'.

På denne måde vil et sikkerhedshul i et tilfældigt plugin til ens CMS ikke
længere kunne kompromitere hele listen af kodeord. Forsøg på at gætte kodeord
vil i hvert enkelt tilfælde kunne ses på autentificerings-servicen.

Med ovenstående seperation mellem adgang til kodord og resten af systemet
mener jeg at man får et mere sikkert system ved at have adgang til kodeord i
klartekst og bruge en challenge/response protokol end med et system hvor man
gemmer brugernes kodord i hashet form, men sender denne form over netværket.

Kommentarer (32)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Peter Mogensen

Måske forstår jeg ikke helt dit eksempel, men...

Hvis API et eller andet sted i stakken er "SELECT authenticate(username,...)"
Så vil dette interface jo stå overfor præcis samme valg som du nævnte med challenge/response vs. PLAIN ovenfor.
Enten skal argumentet til authenticate() være det rå password, så det kan verificeres i databasen, eller også skal der udstedes en challenge. I begge scenarier kræver det at kalenderen af authenticate() kender det rå password. Dvs. det udelukker at der højere oppe i stakken er brugt en C/R mekanisme, der ikke får sit nonce fra databasen.

Et godt design vil derfor minimere mængden af kode der har adgang til hele listen af kodeord.

Det er selvfølgelig altid rigtigt. Men det er værd at overveje at et evt. distribueret system hvor den service skal kunne tilgås mange steder og ikke kun centralistisk, automatisk vil gøre at der er mange angrebspunkter.

  • 0
  • 0
Peter Lind

Det er selvfølgelig altid rigtigt. Men det er værd at overveje at et evt. distribueret system hvor den service skal kunne tilgås mange steder og ikke kun centralistisk, automatisk vil gøre at der er mange angrebspunkter.

Det giver vel også dermed anledning til at vende designet på hovedet: i stedet for at du kan logge på systemet fra 10 forskellige steder, der alle sender password videre til autentificerings-servicen, så sender de steder dig videre til det ene sted hvor du autentificeres - og så tjekker de forskellige services bagefter internt om du er korrekt autentificeret.

  • 1
  • 0
Peter Mogensen

så sender de steder dig videre til det ene sted hvor du autentificeres

Ja. Men ...
Det er et spørgsmål om hvilke fejl-scenarier og sikkerhedsproblemer det skal være tolerant overfor og sikkert i.
Hvis du f.eks. ønkser at systemet skal have nogle HA egenskaber, der ikke tillader "det ene sted" som et single-point-of-failure, så skal du til at overveje om dit design kan distribueres sikkert.

  • 0
  • 0
Ivan Skytte Jørgensen

Jeg har tidligere kigget på SCRAM, og det ser rigtig godt ud. Men det irriterede mig at de eneste implementationer jeg kunne finde var syltet ind i LDAP eller OS-user authentication. Og jeg kunne ikke finde en eneste implementation til client-side i javascript. Det ville være godt hvis der var nogle stand-alone implementationer, så man lettere kunne integrere det med sin ikke-LDAP ikke-OS applikation.

Så jeg tror at det største praktiske problem for at få implementeret mere sikker autentificering er manglen på libraries som er lette at integrere. Når der ingen lette libraries er, så er det forståeligt at de fleste udviklere siger "hul i det - vi bruger blot klartekst password", hvilket er ret uheldigt.

Det er også en komplicering at nogle former for autentificering er uomgængeligt syltet ind i andre behov. F.eks. hvis man implementerer EAP-TTLS/MsCHAPv2, så skal man sørme også have snablen dybt nede i TLS-sessionen for at kunne udlede MSK/EMSK, som skal bruges til WPA-Enterprise keys til wifi. Eller f.eks. nogle libraries sammenkobler autentificering med autorisering.

  • 0
  • 0
Torben Mogensen Blogger

På mobiltelefoner kan man også bruge en sekvens af streger i et 3x3 grid som nøgle. Sikkerheden er ikke voldsomt stor, da der ikke er et kæmpestort antal muligheder, men hvis man for det meste har sin telefon på sig (hvor andre ikke kan røre den) er det O.K.

  • 0
  • 0
Martin Kristiansen

For at opnå dette kan man hashe kodeordet sammen med en vilkårlig streng som serveren vælger og en streng som klienten vælger. Et godt udgangspunkt er at se på RFC 5802 (SCRAM), men ideen er den samme som HTTP Digest mode hvis man anvender MD5-session metoden.

Denne slags challenge/response protokoller kræver dog at serveren har adgang til brugerens kodeord og ikke bare en hash af kodeordet. Det stiller nogle højere krav til opbevaringen af kodeord på serveren.

Den korrekte metode er at gemme et sha-256 hash af passwordet på serveren.

Når klienten får en challenge streng fra serveren, hasher klienten først det indtastede password med sha-256 inden response genereres ved hashing af password-hashet og challenge-strengen.

Oprettelse af bruger kræver self. krypteret forbindelse, men efterfølgende autorisering gør ikke

  • 0
  • 5
Christian Schmidt

Det betyder at en angriber der kan lytte med kan opsnappe kodeordet og bruge det senere. Det kan enten ske fordi man har glemt at bruge SSL, fordi SSL er noget juks eller fordi brugeren forbinder sig til et forkert eller kompromiteret server.

Hvis brugeren sender adgangskoden til en forkert eller kompromiteret server, nytter SCRAM og lign. ikke noget.

  • 2
  • 0
Ivan Skytte Jørgensen

Hvis brugeren sender adgangskoden til en forkert eller kompromiteret server, nytter SCRAM og lign. ikke noget


Der bør du være mere nuanceret.
1: Ved etableringen af passwordet (brugeroprettelse) er der en risiko hvis serveren er kompromiteret, ja. De fleste mekanismer undviger problemet ved at antage at brugeroprettelsen inkl. password er sket "på anden sikker vis", dvs. out-of-band.
2: Hvis man kan kompromitere serveren inkl. webinterface, så kan man narre brugeren til at indtaste password og dermed opsnappe klartekst-passwordet.

Der hvor SCRAM hjælper er:
- hvis serveren efterfølgende bliver kompromiteret, så er klartekst passwordet stadig sikkert.
- hvis hverken client eller ser ver kompromitteret, så opnår man ikke ret meget ved at aflure klartekst-trafikken.

  • 0
  • 0
Christian Schmidt

Brugeren sender slet ikke den shared-secret med en C/R protokol.
Så hvad tænker du konkret på?


Jeg tænker på punkt 2, som Ivan Skytte Jørgensen nævner.

Hvis serveren er kompromiteret på en måde, så uvedkommende kan ændre i sitets HTML/JavaScript, kan vedkommende deaktivere alle sikkerhedsmekanismer og derved få adgang til adgangskoden i klar tekst, når brugeren forsøger at logge ind.

Hvis brugeren går ind på den forkerte server, fx et phishing-site på et typo-squatted domæne, kan ejeren af phishing-sitet ligeledes få adgang til alt, brugeren taster ind.

  • 0
  • 0
Ivan Skytte Jørgensen

Kompromiterede klienter er et næsten uløseligt problem. Det er ikke begrænset til webinterfaces, f.eks. vil en kompromiteret unix-login-program give samme effekt: klartekst password kan opsnappes. En kompromiteret klient er dybest set et MITM-angreb mellem brugeren og serveren.
Skaderne kan begrænses en smule ved at bruge two-factor challenges (SMS, tokencard), så den ondsindede klient kun kan kompromitere een session ad gangen i stedet for alle sessioner.

Problemet er dog størst ved webinterfaces fordi browsere gladeligt henter den ondsindede javascript/java/flash-klient. Det undrer mig at man ikke i større stil bruger http digest authentication (rfc2617), hvor passwordprompten kommer fra browseren selv og ikke en tilfældig webside. Jeg stoler mere på min browser end det indhold som browseren henter og viser.

  • 2
  • 0
Peter Mogensen

Hvis brugeren går ind på den forkerte server, fx et phishing-site

ok... klart.. men så har du også taget problemet til at inkludere beskyttelse imod phishing.
Og da - som du selv siger - man aldrig kan stole på JavaScript:
http://matasano.com/articles/javascript-cryptography/

... ja så bør man altid kun bero sig på browser-indbygget funktionalitet.

Det er selvfølgelig en trist situation - grænsende til det upraktiske - men HTTP Digest beskytter faktisk imod at serveren er kompromiteret. Ihvertfald hvis vi antager at den kompromitering ikke har givet den adgang til server-side versionen af password.

Alternativt skulle man bruge en zero-knowledge protokol, så heller ikke serveren kendte passwordet.

  • 0
  • 0
Peter Mogensen

En kompromiteret klient er dybest set et MITM-angreb mellem brugeren og serveren.

Jeg vil nok mene at det er en helt særlig klasse at trælse "MiTM-angreb", hvis angriberen har fået skudt sig ind der hvor han principielt styrer klientens GUI med grafik og key-logger og det hele :)
Faktisk tror jeg mange MiTM-angribere ville ønske de bare kunne læse brugerens tastetryk og disk. Det er da meget nemmere.

  • 0
  • 0
Anders Lorensen

I disse cloud tider, hvor server siden ligger i en cloud. Ja selv klienten kan ligge i en cloud når vi snakker VDI / Virtual desktops. Så er der udfordringer som slet ikke nævnes i ovenstående. Gemmes passwords ukrypteret i hukommelsen eller på disk, ja så har cloud udbyderen adgang til disse via snapshots, uden at du kan se at tingene har været kompromiteret i en log. Ja selv krypteringsnøgler skal jo nærmest være krypteret for ikke at disse er i fare.

Der findes tusindvis af cloud udbydere. Store som små er cloud administratorene nøglepersoner med adgang til meget mere end de burde. Kan man stole på disse folk? Er det lavtlønnede folk fra østen der sidder og har adgang til vores ting? Eller er det højtuddannede landsmænd med sikkerhedsgodkendelserne i orden? Ja vi ved det ofte ikke, og derfor mener jeg at en protokol bør tage hensyn til dette.

Så er spørgsmålet bare om det overhovedet er muligt, teoretisk eller praktisk at beskytte sig imod dette. For det svarer jo til at både klient og server er kompromiteret permanent.

  • 1
  • 0
Peter Mogensen

Man må nok lige erindre at kvaliteten af authentikering er komplet ligegyldig, hvis den angriber man gerne vil beskytte sig imod principielt har adgang til data udenom authentikeringssystemet.

Så hvis du har dine ting liggende i "skyen", og bekymrer dig om om folk med adgang til driften af skyen kan tilgå dine data, så er der nok ikke så meget authentikering, men derimod kryptering af dine data, der er interessant.
Sagt på en anden måde... Hvis din bekymring er om passwords ligger ukrypteret i hukommelsen i skyen, så burde du vel også bekymre dig om at data selv (som authentikeringen giver adgang til) ligger ukrypteret i hukommelsen?

  • 1
  • 0
Peter Makholm

Jeg undrer mig lidt over at du sætter obfuskering af password som sidste skridt i forarbejdet på klienten.


I en ideel verden vil en bruger til hvert enkelt system vælge et kodeord bestående af 48-64 tilfældige bits. Men verden er ikke ideel, så derfor bliver vi nød til at hjælpe brugeren.

Et af de problemer vi skal løse er at mange brugere vælger det samme kodord til flere forskellige systemer. Det omgår vi ved at blande det kodeord brugeren vælger med noget vi som service-ejere vælger inden det bliver sendt til os.

Dermed kan vi hjælpe til at brugeren ikke bliver yderligere kompromiteret hvis det uheldige skulel ske at vores service bliver kompromiteret. Derfor er det også nødvendigt at denne obfuskering sker på klienten.

  • 0
  • 0
Peter Makholm

Enten skal argumentet til authenticate() være det rå password, så det kan verificeres i databasen, eller også skal der udstedes en challenge. I begge scenarier kræver det at kalenderen af authenticate() kender det rå password.


Korrekt, de challenge/response protokoller jeg umidelbart kender kræver at serveren gemmer nok information til at den kan udgive sig for at være klienten i samme protokol — det vil i et eller andet omfang sige det rå kodeord.

Nogle protokoller starter med at hashe kodeordet og så lade serveren gemme denne hash. Det har samme effekt som den obfuskering jeg placerer som et del af forarbejdet.

Hvordan challenge genereres er jeg også kommet til at springe let og elegant hen over. Det afhænger noget af protokollen, hvis man har unikke sessioner er det indlysende at lade et sessions-id indgå i challenge ellers afhænger det lidt af hvor meget state man vil vedligeholde. Enten kan man generere helt unikke og tilfældige challenges og sørge for at de ikke bliver genbrugt eller også kan man basere sin challenge på en tidskode.

Men i hvert fald kræver man et API til at udstede challenge og at authentificat-metoden har en mulighed for at validere at et challenge nu også er (eller kan være) udstedt til den aktuelle bruger.

  • 0
  • 0
Peter Makholm

Den korrekte metode er at gemme et sha-256 hash af passwordet på serveren.

Når klienten får en challenge streng fra serveren, hasher klienten først det indtastede password med sha-256 inden response genereres ved hashing af password-hashet og challenge-strengen.

I det tilfælde betragter jeg sha-256 hashen af det brugeren indtaster som det egentlige kodeord, da det er dette der er tilstrækkeligt til at man kan gennemføre autentificeringen.

Derfor anser jeg denne hashing som forarbejde på klienten og ikke en del af den egentlige autentificerings-protokol.

  • 0
  • 0
martin -

Uden lige at blande mig i de tekniske aspekter, er "autentificering" så virkelig det rigtige ord? I så fald mangler industrien virkelig at blive enige om en dansk oversættelse af "authentication", der både kan læses og udtales.

Jeg hælder mest til ordet "autentikering", der deler forstavelser med det almindeligt kendte ord "autentisk", og som ikke introducerer unødige h'er eller c'er.

  • 1
  • 0
Rasmus Iversen
  • 0
  • 0
Peter Lind

I en ideel verden vil en bruger til hvert enkelt system vælge et kodeord bestående af 48-64 tilfældige bits. Men verden er ikke ideel, så derfor bliver vi nød til at hjælpe brugeren.

Et af de problemer vi skal løse er at mange brugere vælger det samme kodord til flere forskellige systemer. Det omgår vi ved at blande det kodeord brugeren vælger med noget vi som service-ejere vælger inden det bliver sendt til os.

Dermed kan vi hjælpe til at brugeren ikke bliver yderligere kompromiteret hvis det uheldige skulel ske at vores service bliver kompromiteret. Derfor er det også nødvendigt at denne obfuskering sker på klienten.

Jeg tror vi har en fuldstændig forskellig opfattelse af server og klient. I web-øjemed er klient for mig browseren - og serveren er webserveren.

Når du skriver "blande det kodeord brugeren vælger med noget vi som service-ejere vælger inden det bliver sendt til os." så har du enten lavet en sproglig fejl ("vi vælger det inden det bliver sendt til os"), der er tale om en præ-genereret salt fra serverens side, sendt til klienten og så sendt tilbage igen, eller klienten genererer en salt dynamisk. Eller et fjerde scenarie jeg ikke kan gennemskue.

I det omfang klienten genererer en salt dynamisk, så skal denne også sendes med sammen med password ved oprettelse af brugeren - eller, alternativt, gemmes på klienten så samme "blanding" kan ske hver gang fremover. Er det første tilfældet, så har du givet angribere mulighed for at bestemme salt hvilket er skidt. Er nummer to tilfældet, så har du mistet kontrollen med hvordan brugerens hemmelige data gemmes, hvilket er endnu værre.

Så jeg forstår nok bare ikke hvorfor den salt du vil blande i passwordet skal genereres på klienten (i browseren) og ikke på serveren. Hvilket så muligvis hænger sammen med at vi måske snakker helt forbi hinanden med hensyn til klient og server.

Enlighten me :D

  • 1
  • 1
martin -

@Rasmus Iversen:
Vil ikke afvise at jeg er i mindretal. Og hvad sproglige spørgsmål angår, er det jo flertallet der i sidste ende bestemmer.

Men efter at have tænkt over det, har jeg fundet ud af hvorfor "autentficering" skurrer i mit øre. Det er slet og ret en skæv oversættelse af det engelske ord "authentication".

Så vidt jeg kan slutte (uden at være lingvist), så svarer den engelske endelse -cate til dansk -kere (eller, i sjældnere tilfælde, -cere)
Eksempler: confiscate=konfiskere; decicate=dedikere; communicate=kommunikere; abdicate=abdicere

På samme måde svarer den engelske endelse -fy til den lidt tungere danske endelse -ficere
Eksempler: verify=verificere; classify=klassificere; anglify=anglificere

Så den danske oversættelse "autentificere" implicerer faktisk, at det engelske ord er "authentify", hvis oversættelsen skal være direkte.

Nu kan der selvfølgelig være adskillige grunde til at oversættelsen ikke er direkte:
1) "Autentificere" er et ord, der længe har været brugt på dansk, og altså ikke en direkte oversættelse af "authenticate"
2) Man har bevidst rettet en fejl ved oversættelsen, da man har ment at authentify/autentificere er et mere dækkende begreb
3) Sproglig sjusk

Det er kun den sidste, jeg opponerer imod

  • 0
  • 0
Peter Makholm

Jeg hælder mest til ordet "autentikering", der deler forstavelser med det almindeligt kendte ord "autentisk", og som ikke introducerer unødige h'er eller c'er.

Jeg skal ikke afvise autentikering og jeg er også ofte stødt på denne form, men...

Det ord vi afleder fra er 'autenticitet' og tilføjer endelsen '-ficere'. Autenticitet betyder ægthed og vi søger netop at verificerer ægtheden af den identitet brugeren påkalder sig. Endelsen '-ficere' betyder at 'demonstrere en bestemt egenskab', hvilket er meget passende. Tilsvarende konstruktioner er netop verificere (hvor vi dog ikke har en selvstændig form af det latinske varus) og identificere.

At reducere 'autenticificere' til 'autentificere' ser jeg som en meget naturlig optimering af sproget. Den slags sker ofte.

Dermed har vi to mulige konstruktioner der begge kan betyde præcis det samme. Det er absolut ikke et enestående tilfælde og ofte er det lidt tilfældigt hvilken konstruktion der bliver udnævnt til at være korrekt. I nogle tilfælde ender de to konstruktioner med at have forskellige betydninger (hvilket ungdommen 30 år senere glemmer og dermed forgår sproget – O tempora O mores).

Mit bud på korrekt dansk er at bruge konstruktionen der lægger sig tæt op ad identitet/identificere. Når man logger ind skal man have et brugernavn for at identificere sig og et kodeord for at autentificere sig. (Hvorefter modtageren verificerer begge dele).

(Jeg beklager dybt at jeg enkelte gange er kommet til at indsnige et dybt unødvendigt 'h'. Det er absolut en fejl)

  • 0
  • 0
Steffen Postas

Hvis dit system der håndtere brugerinput, er korrekt sikret mod eksploits til det punkt at man er nød til at have databasen, så er det den vej man skal beskytte sig.
Desuden, har du en applikation der kan kontakte en server, hvor at serverens programmer derefter snakker med en database, så er det da et gigantisk helvede at skulle sørge for at serveren både kan oprette entries, men kun udføre specifikke procedures. Istedet bør databasen konstrueres på en sådan måde, at skulle der være nogen der fik fat i den, så er data'en stadig ikke kompromiteret.

Hvis du sørger for at beskytte eventuelle personlige oplysninger, såvel som både kodeordshash og andet, med nøgler, så er man da så langt.
Men det kan man også bare droppe helt, og istedet sørge for at der tages backup af data, og at der efter indtrængen bliver både lukket huller, såvel som at det data der er kompromiteret ikke kan benyttes til noget.
En løsning er blandt andet at resette alle kodeord til et pseudotilfældigt hash, og derefter sende requests ud til brugerne om at nulstille deres kodeord grundet kompromitering.
Kompromitering er alligevel ikke 100% noget der kan undgås, så længe man er forbundet til nettet. Så er det bedre blot at benytte sig af de bedste implementeringer af de sikreste algoritmer, sørge for at kun benytte "sanitised input", osv., og ellers have en indbygget mulighed for at undslippe grebet af de kompromiterendes hænder.

  • 0
  • 0
Log ind eller Opret konto for at kommentere
IT Company Rank
maximize minimize