Den definitive guide til formularbaseret autentificering af websteder
Formularbaseret godkendelse til websteder
Vi mener, at Stack Overflow ikke kun bør være en ressource for meget specifikke tekniske spørgsmål, men også for generelle retningslinjer for, hvordan man løser variationer af almindelige problemer. "Formularbaseret autentificering til websteder" bør være et godt emne for et sådant eksperiment.
Det bør omfatte emner som f.eks:
- Hvordan man logger ind
- Hvordan man logger ud
- Hvordan man forbliver logget ind
- Håndtering af cookies (herunder anbefalede indstillinger)
- SSL/HTTPS-kryptering
- Sådan opbevares adgangskoder
- Brug af hemmelige spørgsmål
- Funktioner til at glemme brugernavn/adgangskode
- Brug af nonces til at forhindre cross-site request forgeries (CSRF)
- OpenID
- "Husk mig" afkrydsningsfelt
- Auto-udfyldning af brugernavne og adgangskoder i browseren
- Hemmelige URL'er (offentlig URL beskyttet af digest)
- Kontrol af passwordets styrke
- Validering af e-mail
- og meget mere om formularbaseret autentificering...
Det bør ikke omfatte ting som:
- Roller og autorisation
- grundlæggende HTTP-godkendelse
Hjælp os venligst ved at:
- Foreslå underemner
- Indsende gode artikler om dette emne
- Redigering af det officielle svar
5304
3
DEL I: Sådan logger du ind
Vi antager, at du allerede ved, hvordan man opbygger en HTML-formular til login+password, som sender værdierne til et script på serversiden til godkendelse. Afsnittene nedenfor vil omhandle mønstre for god praktisk auth, og hvordan man undgår de mest almindelige sikkerhedsfælder. To HTTPS or not to HTTPS? Medmindre forbindelsen allerede er sikker (dvs. tunneleret gennem HTTPS ved hjælp af SSL/TLS), vil værdierne i din login-formular blive sendt i klartekst, hvilket gør det muligt for enhver, der aflytter linjen mellem browser og webserver, at læse logins, mens de passerer igennem. Denne form for aflytning foretages rutinemæssigt af regeringer, men generelt vil vi ikke tage fat på 'ejede' ledninger andet end at sige dette: Bare brug HTTPS. Den eneste praktiske måde at beskytte sig mod aflytning/pakkesnifning under login er i bund og grund ved at bruge HTTPS eller en anden certifikatbaseret krypteringsordning (f.eks. [TLS][1]) eller en gennemprøvet & testet challenge-response-ordning (f.eks. den [Diffie-Hellman][2]-baserede SRP). Alle andre metoder kan let omgås af en aflytningsangriber. Hvis du er villig til at være lidt upraktisk, kan du naturligvis også anvende en form for to-faktor-autentifikationsordning (f.eks. Google Authenticator-appen, en fysisk kodebog i koldkrigsstil eller en RSA-nøglegenerator-dongle). Hvis det anvendes korrekt, kan dette fungere selv med en usikker forbindelse, men det er svært at forestille sig, at en udvikler vil være villig til at implementere to-faktor auth, men ikke SSL. (ikke) Roll-your-own JavaScript-kryptering/hashing I betragtning af de opfattede (om end nu [undgåelige][27]) omkostninger og tekniske vanskeligheder ved at oprette et SSL-certifikat på dit websted, er nogle udviklere fristet til at rulle deres egne hashing- eller krypteringsordninger i browseren for at undgå at sende klartekstlogins over en usikker forbindelse. Selv om dette er en ædel tanke, er det i bund og grund ubrugeligt (og kan være en [sikkerhedsbrist][3]), medmindre det kombineres med et af ovenstående - dvs. enten at sikre linjen med stærk kryptering eller bruge en gennemprøvet challenge-response-mekanisme (hvis du ikke ved, hvad det er, skal du bare vide, at det er et af de mest vanskelige at bevise, mest vanskelige at designe og mest vanskelige at implementere koncepter inden for digital sikkerhed). Selv om det er sandt, at hashing af adgangskoden kan være effektivt mod adgangskodeafsløring, er det sårbart over for replay-angreb, Man-In-The-Middle-angreb / hijackings (hvis en angriber kan injicere et par bytes i din usikrede HTML-side, før den når din browser, kan de simpelthen kommentere hashing i JavaScript) eller brute-force-angreb (da du giver angriberen både brugernavn, salt og hashet adgangskode). CAPTCHAS mod menneskeheden [CAPTCHA][4] er beregnet til at afværge en bestemt kategori af angreb: automatiseret ordbog/brute force trial-and-error uden menneskelig operatør. Der er ingen tvivl om, at dette er en reel trussel, men der er måder at håndtere det problemfrit på, som ikke kræver en CAPTCHA, specielt korrekt designede server-side login throttling ordninger - vi vil diskutere dem senere. Du skal vide, at CAPTCHA-implementeringer ikke er ens; de er ofte ikke menneskeligt opløselige, de fleste af dem er faktisk ineffektive mod bots, alle er ineffektive mod billig arbejdskraft fra den tredje verden (ifølge [OWASP][5] er den nuværende sweatshop-rate 12 dollars pr. 500 tests), og nogle implementeringer kan være teknisk set ulovlige i nogle lande (se [OWASP Authentication Cheat Sheet][6]). Hvis du skal bruge en CAPTCHA, så brug Google's [reCAPTCHA][7], da den pr. definition er OCR-hård (da den bruger allerede OCR-misklassificerede bogscanninger) og forsøger meget hårdt at være brugervenlig. Personligt har jeg en tendens til at finde CAPTCHAS irriterende og bruger dem kun som en sidste udvej, når en bruger har undladt at logge ind et antal gange og throttling-forsinkelserne er maxet ud. Det sker sjældent nok til at være acceptabelt, og det styrker systemet som helhed. Lagring af adgangskoder/verificering af logins Dette er måske endelig almen viden efter alle de meget omtalte hacks og lækager af brugerdata, vi har set i de seneste år, men det skal siges: Gem ikke adgangskoder i klartekst i din database. Brugerdatabaser bliver rutinemæssigt hacket, lækket eller fundet gennem SQL-injektion, og hvis du gemmer rå kodeord i klartekst, er det straks slut med hensyn til din login-sikkerhed. Så hvis du ikke kan gemme adgangskoden, hvordan kontrollerer du så, at kombinationen af login+adgangskode, der er POSTet fra loginformularen, er korrekt? Svaret er hashing ved hjælp af en [key derivation function][24]. Hver gang en ny bruger oprettes eller et password ændres, tager du passwordet og kører det gennem en KDF, såsom Argon2, bcrypt, scrypt eller PBKDF2, og forvandler cleartext passwordet ("correcthorsebatterystaple") til en lang, tilfældigt udseende streng, som er meget mere sikker at gemme i din database. For at verificere et login kører du den samme hashfunktion på det indtastede password, denne gang ved at indsætte saltet og sammenligne den resulterende hashstreng med den værdi, der er gemt i din database. Argon2, bcrypt og scrypt gemmer allerede saltet sammen med hash-strengen. Se denne [artikel][23] på sec.stackexchange for mere detaljerede oplysninger. Grunden til at der bruges et salt er, at hashing i sig selv ikke er tilstrækkeligt -- du ønsker at tilføje et såkaldt 'salt' for at beskytte hashen mod [regnbuetabeller][8]. Et salt forhindrer effektivt, at to passwords, der passer nøjagtigt sammen, bliver gemt som den samme hash-værdi, hvilket forhindrer, at hele databasen bliver scannet på én gang, hvis en angriber udfører et password guessing attack. En kryptografisk hash bør ikke anvendes til lagring af adgangskoder, fordi brugervalgte adgangskoder ikke er stærke nok (dvs. normalt ikke indeholder tilstrækkelig entropi), og et angreb med adgangskoder kan gennemføres på relativt kort tid af en angriber, der har adgang til hash-koderne. Det er derfor, der anvendes KDF'er - disse er effektivt ["stretch the key"][25], hvilket betyder, at hvert password-gæt, som en angriber foretager, medfører flere gentagelser af hash-algoritmen, f.eks. 10.000 gange, hvilket får angriberen til at gætte passwordet 10.000 gange langsommere. Sessionsdata - "Du er logget ind som Spiderman69" Når serveren har verificeret login og password i forhold til din brugerdatabase og fundet et match, skal systemet kunne huske, at browseren er blevet autentificeret. Dette faktum bør kun gemmes på serversiden i sessionsdataene.
Endelig artikel
Afsendelse af legitimationsoplysninger
Den eneste praktiske måde at sende legitimationsoplysninger 100 % sikkert på er ved at bruge [SSL][1]. Det er ikke sikkert at bruge JavaScript til at hashe kodeordet. Almindelige faldgruber for klient-side password hashing:
Lagring af adgangskoder
Du må aldrig gemme adgangskoder som klartekst i databasen. Ikke engang hvis du er ligeglad med sikkerheden på dit eget websted. Antag, at nogle af dine brugere vil genbruge adgangskoden til deres online bankkonto. Så gem den hashede adgangskode og smid den originale væk. Og sørg for, at adgangskoden ikke vises i adgangslogfiler eller programlogfiler. OWASP anbefaler brugen af Argon2 som dit første valg for nye applikationer. Hvis dette ikke er tilgængeligt, bør PBKDF2 eller scrypt bruges i stedet. Og endelig, hvis ingen af de ovennævnte er tilgængelige, skal du bruge bcrypt. Hashes i sig selv er også usikre. F.eks. betyder identiske passwords identiske hashes - dette gør hash-opslagstabeller til en effektiv måde at knække mange passwords på én gang. Gem i stedet den saltede hash. En salt er en streng, der tilføjes til adgangskoden før hashing - brug en anden (tilfældig) salt pr. bruger. Saltet er en offentlig værdi, så du kan gemme dem sammen med hash'en i databasen. Se her for mere om dette. Det betyder at du ikke kan sende brugeren deres glemte passwords (fordi du kun har hash'en). Du må ikke nulstille brugerens adgangskode, medmindre du har autentificeret brugeren (brugerne skal bevise, at de kan læse e-mails, der sendes til den gemte (og validerede) e-mailadresse).
Sikkerhedsspørgsmål
Sikkerhedsspørgsmål er usikre - undgå at bruge dem. Hvorfor? Alt, hvad et sikkerhedsspørgsmål kan, kan en adgangskode gøre bedre. Læs PART III: Brug af hemmelige spørgsmål i [@Jens Roland svar][6] her i denne wiki.
Sessionscookies
Når brugeren logger ind, sender serveren brugeren en session cookie. Serveren kan hente brugernavnet eller id'et fra cookien, men ingen andre kan generere en sådan cookie (TODO forklarer mekanismer). [Cookies kan kapres][7]: de er kun lige så sikre som resten af klientens maskine og anden kommunikation. De kan læses fra disken, sniffes i netværkstrafikken, tages fra et cross-site scripting-angreb, phished fra en forgiftet DNS, så klienten sender sine cookies til de forkerte servere. Send ikke vedvarende cookies. Cookies bør udløbe ved afslutningen af klientsessionen (lukning af browseren eller når du forlader dit domæne). Hvis du ønsker at autologinere dine brugere, kan du indstille en vedvarende cookie, men den skal være adskilt fra en cookie for hele sessionen. Du kan indstille et ekstra flag om, at brugeren er automatisk logget ind og skal logge ind for alvor ved følsomme operationer. Dette er populært blandt shoppingwebsteder, der ønsker at give dig en problemfri, personlig shoppingoplevelse, men stadig beskytte dine finansielle oplysninger. Når du f.eks. vender tilbage til Amazon, viser de dig en side, der ser ud som om du er logget ind, men når du vil afgive en ordre (eller ændre din leveringsadresse, dit kreditkort osv.), beder de dig om at bekræfte din adgangskode. Finansielle websteder som banker og kreditkort har på den anden side kun følsomme data og bør ikke tillade automatisk logon eller en lavsikkerhedstilstand.
Liste over eksterne ressourcer
Først et kraftigt advarsel om, at dette svar ikke passer bedst til netop dette spørgsmål. Det bør bestemt ikke være det bedste svar!
Jeg vil gå videre og nævne Mozillas foreslåede BrowserID (eller måske mere præcist Verified Email Protocol) i en ånd af at finde en opgraderingsvej til bedre tilgange til autentificering i fremtiden.
Jeg vil opsummere det på denne måde:
@
domain" er kortfattet og understøttes af en lang række protokoller og URI-ordninger. En sådan identifikator er naturligvis mest universelt anerkendt som en e-mail-adresse.Dette er ikke strengt taget "formularbaseret autentificering til websteder". Men det er et forsøg på at gå fra den nuværende norm med formularbaseret autentificering til noget mere sikkert: browserstøttet autentificering.