Hash, salt, bcrypt e Argon2: que fai un sitio ben feito co teu contrasinal

Publicado o por David Carrero

Un sitio ben feito non sabe o teu contrasinal. Non é que non queira: é que non pode.

Soa a truco, porque cada vez que o escribes o sistema déixate pasar, e para deixarte pasar ten que comprobar algo. Pero comprobar non é saber. O servidor garda unha pegada calculada a partir do teu contrasinal; cando escribes, volve calcular a pegada e compara. Se coinciden, adiante. En ningún momento fai falla que o contrasinal estea escrito en ningures.

De aí sae o sinal máis claro para distinguir un servizo serio doutro que non o é: se ao esquecelo cho envían por correo, tíñano gardado. E o que está gardado acaba, antes ou despois, en mans doutra persoa.

1979 a idea de non gardalos — e a de facelo amodo

En 1979, Robert Morris e Ken Thompson publicaron en Communications of the ACM «Password Security: A Case History», contando o que fixeran en Unix. É un relato do aprendido, e nel quedan asentadas dúas ideas que hoxe damos por evidentes.

A primeira non a inventaron eles —gardar o hash en lugar do contrasinal xa se facía antes—, pero explicárona mellor ca ninguén: non gardar o contrasinal, gardar o seu hash. Unha función que converte o teu contrasinal nun valor de aspecto arbitrario e que non se pode desandar: do hash non se volve ao contrasinal.

A segunda si é súa: o sal. Un valor aleatorio distinto para cada usuario, que se mestura co contrasinal antes de calcular o hash. Sen sal, dúas persoas co mesmo contrasinal teñen o mesmo hash —o ficheiro roubado convértese nun xogo de buscar repetidos— e, peor aínda, alguén pode precalcular unha táboa xigante unha vez e usala contra todas as bases de datos do mundo. O sal non fai o teu contrasinal máis difícil de adiviñar: fai que adiviñalo non lle sirva a ninguén máis.

Pero nese mesmo artigo hai unha terceira idea, moito menos citada, e é a que importa aquí: fixeron a función deliberadamente lenta. Non por descoido. A mantenta. Cifrar un contrasinal custaba moito máis do que tecnicamente facía falla, porque a quen entra unha vez cada mañá tanto lle ten, e a quen proba millóns de candidatas non.

Aí está todo o asunto nunha frase de hai case cincuenta anos.

Por que a lentitude é a única defensa honesta

Case todas as defensas informáticas son asimétricas: cústanlle pouco a quen se defende e moito a quen ataca. Cifrar é barato; descifrar sen a clave é inviable. O defensor xoga con vantaxe estrutural.

O almacenamento de contrasinais non. Aquí defensor e atacante executan exactamente a mesma función. Ti calculas o hash para comprobar que acertaches; el calcúlao para comprobar se acertou. É a única carreira da seguridade onde os dous corren coas mesmas pernas.

Cando non podes correr máis ca o teu rival, só queda unha xogada: encarecer a pista para os dous. Ti pagas ese custo unha vez por inicio de sesión. El, unha vez por intento. Como necesita ordes de magnitude máis intentos ca ti, a mesma factura afectávos de forma incomparable. Iso é o que fai un hash de contrasinais.

Por iso as funcións rápidas e boísimas para outras cousas —SHA-256, por exemplo— son unha elección pésima aquí. Son rápidas. Rápido é exactamente o que non queremos.

1999 bcrypt, e o custo como peza axustable

O problema dunha función lenta é que o hardware non queda quieto. O que en 1979 era lento, despois é instantáneo. Unha defensa calibrada contra os ordenadores dunha década avellenta soa, sen que ninguén a toque.

En 1999, Niels Provos e David Mazières presentaron bcrypt na USENIX Annual Technical Conference, cun título que xa o di todo: A Future-Adaptable Password Scheme.

A idea: que o custo non estea na función, senón nun parámetro. bcrypt leva un factor de custo que decides ti e que se garda xunto ao hash. Se o hardware se volve moito máis rápido, subes o número e os contrasinais novos volven custar o que deben, sen cambiar de algoritmo.

É un deseño que asume a súa propia obsolescencia e se prepara para ela. Hai poquísimo software que faga iso.

2009 scrypt, e o descubrimento de que o tempo non chega

bcrypt encarece o tempo de cálculo, e funcionou. Ata que o atacante deixou de usar o mesmo tipo de máquina ca o defensor.

Un servidor ten uns poucos núcleos rápidos e de propósito xeral. Unha GPU ten unha barbaridade de núcleos pequenos facendo o mesmo en paralelo, e un ASIC vai máis lonxe: silicio fabricado para executar unha soa operación. Se a túa defensa consiste en «esta operación custa X», alguén pode construír hardware que faga moitas X á vez. O defensor segue co seu servidor.

En 2009, Colin Percival presentou scrypt con outro argumento: Stronger Key Derivation via Sequential Memory-Hard Functions. Se o custo está só no cálculo, paralelízase. Pero a memoria non se regala. Obriga a función a usar moita RAM e a percorrela de forma que non se poida esquivar, e de súpeto o atacante non pode multiplicar núcleos: cada núcleo necesitaría a súa propia memoria. E os núcleos dunha GPU teñen moita potencia e moi pouca RAM para cada un.

Percival non encareceu a operación. Encareceu o hardware necesario para repetila en masa. É un cambio de nivel: atácase o orzamento do atacante, non o seu reloxo.

2015 Argon2, e o final do debate

Que existisen varias opcións razoables non lle axudaba a ninguén a escoller. Así que a comunidade criptográfica fixo o que adoita facer cando quere rematar un asunto: un concurso público. A Password Hashing Competition recibiu candidatos, sometounos a análise aberta durante anos e en 2015 proclamou gañador a Argon2.

Argon2 herda as dúas leccións: custo en tempo e custo en memoria, ambos os dous axustables por separado, máis o número de fíos. Non é maxia. É a síntese ordenada de 1979, 1999 e 2009, revisada por xente cuxo traballo consistía en intentar rompela.

Que significa isto para ti

Case nada disto está nas túas mans, e convén dicilo: o hash escólleo o sitio, non ti. Podes ter o mellor contrasinal do mundo e que cho garden en texto plano nunha folla de cálculo.

Pero o outro lado da ecuación si depende de ti. Todo este edificio —o sal, o custo, a memoria— serve para encarecer cada intento do atacante. O que decide cantos intentos precisa é o teu contrasinal. Se está nun dicionario, ningunha función lenta te salva: caerá entre os primeiros. Se é longo e non se parece a nada, ese prezo por intento multiplícase por un número de intentos que non colle no tempo dispoñible.

Por iso o noso xerador mide bits de entropía e non porcentaxes, e por iso o comprobador busca o teu contrasinal en dicionarios antes que contar símbolos. Eles poñen o prezo por intento; ti pos o número de intentos. Ningunha das dúas metades funciona sen a outra.

E si: segue sendo a mesma pregunta de sempre. Só que agora, do outro lado, alguén intenta respondela moitas veces por segundo, e levamos desde 1979 intentando que cada unha desas veces lle custe cartos.


Fontes: R. Morris e K. Thompson, «Password Security: A Case History», Communications of the ACM, 1979 · N. Provos e D. Mazières, «A Future-Adaptable Password Scheme», USENIX Annual Technical Conference, 1999 (bcrypt) · C. Percival, «Stronger Key Derivation via Sequential Memory-Hard Functions», 2009 (scrypt) · Password Hashing Competition, gañador Argon2, 2015.

← Volver ao blog