Java >> Java tutorial >  >> Tag >> SQL

BCrypt på klientsiden, gem salt og hash separat i MySQL-databasen

Klienten er angriberen. Gå rundt på dit kontor, mens du synger den sætning 144 gange; sørg for at sætte punktum i din diktion med en lille tromme. På den måde vil du huske det.

På din server sender du Java-kode til at køre på klienten. Den ærlige klient vil køre din kode. Ingen tvinger angriberen til at gøre det også; og du bruger klientgodkendelse, netop fordi du frygter, at klienten måske er en anden, der forsøger at efterligne den normale bruger. Fra dit serversynspunkt ser du kun de bytes, du sender til klienten, og de bytes, der kommer tilbage. Du kan ikke sikre dig, at disse bytes blev beregnet med din kode. Angriberen, som er en ond skurk, er perfekt i stand til at forestille sig ikke køre din kode og i stedet sende det svar, som din server forventer.

Overvej nu en simpel adgangskodegodkendelsesordning, hvor du blot gemmer brugernes adgangskoder i din database. For at godkende skal du bede brugeren om at sende adgangskoden og derefter sammenligne den med det, du har gemt. Dette er meget enkelt. Dette er også meget dårligt:​​det kaldes adgangskoder i almindelig tekst . Problemet er, at ethvert simpelt skrivebeskyttet glimt af databasen (det være sig et SQL-injektionsangreb, et stjålet backupbånd, en hentet gammel harddisk fra en dumpster...) vil give angriberen alle adgangskoder. For at sige tingene klart er fejlen her i at gemme nøjagtigt de værdier i databasen, som, når de sendes fra klienten, giver adgang.

Og din foreslåede ordning? Præcis det samme. Du gemmer i databasen hash-værdien "som den er". Og når klienten sender den nøjagtige værdi, gives der adgang. Selvfølgelig de ærlige klienten vil sende værdien ved at hash en adgangskode. Men lad os se det i øjnene:mange angribere er ikke ærlige mennesker.

Nu er der værdi i at gøre del af hashing på klientsiden. Faktisk er god adgangskode-hashing et våbenkapløb, hvor hashing udføres dyrt med vilje. At aflaste noget af arbejdet på kunderne kan være en god ting. Det fungerer ikke så godt, når klienterne er svage, f.eks. smartphones med Java, eller endnu værre, Javascript (som er en helt anden ting, trods navneligheden).

I så fald skal du køre bcrypt på klienten og gemme ikke bcrypt-outputtet på serveren, men hash af bcrypt-outputtet med en fornuftig hash-funktion (en hurtig som SHA-256 ville være fin). Behandlingen af ​​en adgangskode P ville så være en bcrypt på klienten, derefter en SHA-256 af resultatet, beregnet på serveren. Dette vil presse det meste af CPU-udgifterne på klienten og vil være lige så sikkert som en rå bcrypt for det, den er beregnet til at gøre (se nedenfor).

Jeg forstår, at du vil "kryptere" adgangskoder (hashing er ikke kryptering!), fordi du vil bruge almindelig HTTP. Du kan ikke lide HTTPS af samme grund som alle andre, som er det frygtede SSL-certifikat. At betale 20 dollars om året for et SSL-certifikat ville svare til at få dit skind fjernet med en kartoffelskræller drysset med citronsaft.

Desværre er der ikke noget at komme udenom skrælleren. Som andre har bemærket, selvom du har en bundsolid autentificeringsmekanisme, er rå HTTP stadig ubeskyttet, og en aktiv angriber kan simpelthen vente på, at en bruger godkender og kapre forbindelsen fra det tidspunkt. Et meget almindeligt eksempel på "aktiv angriber" er folk, der blot kører et falsk WiFi-adgangspunkt -- det vil sige et helt rigtigt WiFi-adgangspunkt, men de beholder også muligheden for at kapre forbindelser på ethvert tidspunkt. Dette er en slags angrebsmodel, der ikke kan imødegås uden en omfattende kryptografisk protokol, der strækker sig over alle dataene, ikke kun en indledende godkendelsesmetode. Omtrent den enkleste form for sådan protokol er SSL/TLS. Enhver protokol, der giver de samme garantier, som du absolut har brug for, vil også være lige så kompleks som SSL/TLS og meget sværere at implementere, fordi den i modsætning til SSL/TLS ikke allerede er implementeret i klientsoftwaren.

SSL er der, bare brug det. Med hensyn til citronen, sug den op.

(Hvis de økonomiske omkostninger er en barriere, er der gratis alternativer, såsom Let's Encrypt og pki.io. Hvorvidt de passer til din regning må vise sig, men de er værd at overveje, hvis du virkelig mangler kontanter.)


Hvis du ikke bruger en sikker forbindelse (https), er dit skema fejlbehæftet på flere måder:

  • Den hash-kodede adgangskode er alt, der kræves for at logge ind:du er sårbar over for et gentagelsesangreb;
  • Da forbindelsen ikke er krypteret, kan det javascript, du sender til klienten, manipuleres af angriberen (angriberen kunne bare lade klienten sende adgangskoden tilbage til dem).

Hvis du vil have sikkert login, skal du være i stand til at garantere, at koden på klientsiden er korrekt, og at der ikke er noget af værdi at snuse på ledningen.

Under næsten alle omstændigheder er den bedste løsning til dette at bruge en SSL/TLS-sikret forbindelse. Du skal sende den ikke-hashed adgangskode over denne sikre forbindelse og foretage hashing på serversiden.

Også ved at fjerne $2a$10$ del, forhindrer du dig selv i at øge gentagelsesantallet hvor som helst i fremtiden:fordi du ikke gemmer iterationsantallet, har du ingen metode til at identificere gamle hashes fra nye, efter du har besluttet at øge gentagelsesantallet.

(Hvis du kan garantere klientkoden, du kunne teoretisk bruge SRP, men at få det rigtigt er en kompleks affære, og du vil sandsynligvis mislykkes).


Hvis du sender noget over en ukrypteret HTTP-forbindelse, så antag, at det bliver overvåget og kan blive kompromitteret med MITM.

Hvis nogen skulle MITM dig, kunne de levere deres eget salt og svare med deres egen hash. Derfor kan enhver angriber fuldstændig forfalske din godkendelsesproces.

Du bør hashe adgangskodeserversiden og bruge en sikker forbindelse.


Java tag