Conceito - como fortalecer a proteção das senhas "12345" contra ataques de força bruta (segunda tentativa)

Atualizado: a tentativa novamente falhou .



Sempre quis que um hacker não conseguisse quebrar a senha de outra pessoa no site.

Por exemplo, se um usuário se gabar para um hacker de que sua senha consiste apenas em números, logo o usuário perderá sua conta.



Mas e se o usuário dissesse à esposa sua senha por telefone e o hacker a ouvisse?



O que?! O hacker sabe a senha? Tudo isso é um fiasco. Você pode ajudar esse usuário a dificultar o sequestro de sua conta? Essa questão sempre me preocupou e acho que encontrei uma maneira de fazer isso. Ou redescobriu-o, como costuma acontecer. Afinal, tudo há muito foi inventado antes de nós.



Introdutório



  • O usuário deseja ter uma senha no site "12345".
  • Um hacker pode facilmente adivinhar essa senha.
  • Mas o usuário precisa fazer o login, e o hacker não. Mesmo que o hacker saiba o login e a senha.
  • e nenhum SMS com códigos secretos e intermediários na forma de serviços adicionais. Apenas o usuário e seu site com página de login.
  • e também será relativamente seguro dizer para sua esposa em um trólebus: “Galya, mudei a senha para 123456 no site do nosso login Alice - dizem que é mais popular do que nosso 12345”. E não tenha medo de que sua conta seja hackeada em um segundo.


Como funciona o método? Todos os detalhes estão sob o corte.



O que é necessario?



  • o conceito apenas explica o método de autenticação
  • a implementação requer apenas armazenar " username ", " password ", " salt1 " e " salt2 ". Sim, dois sais.
  • fazer sem tabelas de log e contadores no redis
  • não vamos manter tabelas com endereços IP
  • não vamos usar SMS
  • não bloquearemos as tentativas de login. Como você sabe da minha última tentativa malsucedida, é inútil bloquear a entrada - mesmo se um hacker atingir o limite de tempo, ele simplesmente começará a destruir as senhas de vários usuários ao mesmo tempo. Além disso, o próprio usuário sofrerá com as restrições. Não ligue para ele para fazer o login no seu site com fotos legais?
  • o usuário pode alterar a senha a qualquer momento e torná-la inválida em outros dispositivos. Essa é uma regra comum, mas acho que vale a pena mencionar.
  • você pode tornar o processo de adivinhar uma senha usando um dicionário mais difícil para um hacker (opcional, será mencionado abaixo).


Essência do método



Permita que o usuário tenha a senha "12345", e será mais difícil quebrá-la. Por exemplo, como adivinhar uma senha que se parece com um hash.



Como?



Imagine se o navegador sempre tivesse um salt exclusivo para salgar a senha. Sal para cada usuário. Por que é necessário? Para criptografar. Por exemplo, se você criptografar a string "12345" com o sal "saltalt" em argon2id, obterá "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg". Troque o sal e o hash será diferente. Um algoritmo criptografará as mesmas senhas de maneira diferente, usando um sal diferente para cada uma. Boa.



Mas onde obter esse sal inicialmente? Sim, aqui está ela sentada em frente ao monitor. Deixe-o espremer dois ou três personagens extras e fazer o login de forma humana. Tem um gato correndo por aí? Bem, vamos ter um gato. O que é gato? Esta é a nossa palavra secreta. Nós o enviaremos para o servidor durante o registro, e ele gerará sal para esta palavra. E então ele vai enviar esse sal para nós. É isso - o navegador tem sal. Agora a senha. E também criptografamos a senha e adicionamos o salt que o servidor enviou.



Agora não usamos o capacete "12345". Enviamos um hash e, como cada usuário tem seu próprio sal, o hash é diferente.



Parece que a força bruta ficará doente agora: não só terá que fazer cálculos adicionais e iterar em longas strings de hashes de argônio em vez de números simples, como também cada usuário terá seu próprio hash - agora é inútil tentar a mesma string como senha para verificá-la para todos Comercial. Digamos que três usuários tenham escolhido a mesma senha: 12345. Mas o hash será diferente. Porque cada pessoa tem um sal diferente.



  • O navegador precisa calcular o hash da senha usando o salt que o servidor enviou anteriormente. Ele deve enviar um hash, não a própria senha.
  • O servidor envia salt usando uma palavra secreta conhecida apenas pelo usuário. Pode ser simples. Por exemplo - "gato".
  • Cada usuário deve ter seu próprio sal.
  • Dois usuários que escolheram a mesma palavra secreta devem ter um sal diferente.
  • O servidor não precisa relatar se a palavra secreta correta foi usada e se o salt é correto para este usuário - caso contrário, serão duas senhas simples de força bruta em vez de uma.
  • Se o usuário alterar a palavra secreta, o salt também será alterado.


Ou seja, para proteger sua senha simples, o usuário precisa inventar outra palavra bem simples. Ele insere essa palavra onde deseja ser autenticado e, então, apenas a senha precisará ser inserida. Até que ele limpe os cookies.



  • foi ao site
  • entrada de login e palavra secreta
  • senha inserida
  • pronto


A senha e a palavra secreta podem ser muito simples. Um ou dois personagens. Por exemplo, a senha é 12345 e a palavra secreta 42. E se outra pessoa vier com a palavra secreta 42, então não será assustador.



Como funciona. Conceito passo a passo



Temos os seguintes elementos:



  • servidor web
  • banco de dados e tabela de usuários:



    • Conecte-se
    • password_hash
    • salt_unique_for_each_user
    • salt_for_password
  • navegador do usuário
  • navegador hacker
  • páginas de login e registro no site
  • script que intercepta o evento de envio para o formulário de login


Em seguida, precisamos de dois algoritmos diferentes que podem ser implementados até mesmo no mesmo sistema de criptografia, simplesmente com parâmetros diferentes:



  • ALG1 é um algoritmo de criptografia assimétrica que gera um hash de uma string e salt. ALG1 (str, sal) = hash1. Este algoritmo é usado apenas no servidor.
  • ALG2 é um algoritmo de criptografia assimétrica que gera um hash de uma string e um salt. ALG2 (str, sal) = hash2. Este algoritmo é usado publicamente e deve ser possível implementá-lo no cliente (em nosso exemplo, em javascript).


Além disso, precisamos de mais dois algoritmos mais simples:



  • ALG_SALT é um algoritmo que calcula um salt aleatório como uma string de caracteres. ALG_SALT () = sal. Este algoritmo é usado apenas no servidor.
  • ALG_PASS é um algoritmo que gera uma senha simples aleatória. ALG_PASS () = aprovado. Este algoritmo é usado apenas no servidor.


Eventos passo a passo



  • O usuário vai para a página de cadastro, pois ainda não possui um login.
  • O servidor exibe um formulário com dois campos: login + palavra secreta simples.
  • O usuário seleciona o login - alice
  • O usuário escolhe a palavra secreta - gato
  • O usuário clica no botão Enviar .


O servidor verifica e garante que o usuário alice não esteja presente no banco de dados.



O servidor calcula os seguintes valores:



$salt_unique_for_each_user = ALG_SALT(); //  "saltsalt"
      
      





$salt_for_password = ALG1("cat", $salt_unique_for_each_user); //  "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"
      
      





$user_simple_password = ALG_PASS(); //  "12345"
      
      





$user_simple_password_hashed = ALG2($user_simple_password , $salt_for_password); //  "$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ"
      
      





O servidor cria um registro na tabela de usuários e salva os dados:



INSERT INTO `users` 
(
login, 
password_hashed, 
salt_unique_for_each_user, 
salt_for_password
) 
VALUES 
(
"alice", 
"$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ", 
"saltsalt", 
"$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"
).

      
      





O servidor mostra ao usuário uma página de sucesso de registro com a mensagem: “O usuário alice foi criado com sucesso. Use a senha temporária 12345 para fazer o login. "



O usuário grita alegremente: “Hurrah, me cadastrei no site com o apelido alice e me deram a senha 12345. Que senha engraçada e simples!”. Mas o apartamento do usuário tem um isolamento acústico muito ruim, e seu vizinho hacker ouviu de tudo.



  • O hacker insere o endereço do site em seu navegador.
  • O navegador do hacker envia cookies vazios.
  • O servidor verifica a solicitação do hacker para ver se há um cookie "salt". Não a encontra.
  • Antes que o hacker envie o nome de usuário e a senha roubados, o navegador precisa saber o salt para criptografar a senha com ele.
  • O navegador do hacker ainda não armazena o sal no cookie "sal".
  • O servidor envia um formulário de login com dois campos: login + palavra secreta para permitir ao usuário obter o salt.


O hacker está confuso. Vamos deixá-lo por enquanto.



  • O usuário retorna à página de login.
  • O navegador do usuário envia cookies vazios.
  • O servidor verifica a solicitação do usuário para ver se há um cookie "salt". Não a encontra.
  • Antes que o usuário envie um nome de usuário e uma senha, o navegador deve conhecer o salt para criptografar a senha com ele.
  • O navegador do usuário ainda não armazena o sal no cookie "sal".
  • O servidor envia um formulário de login com dois campos: login + palavra secreta para permitir ao usuário obter o salt.
  • O usuário digita login - alice , secret - cat e clica no botão " Enviar ".


O servidor recebe a solicitação e vê que, em vez da senha, uma palavra secreta foi enviada.



  • alice `salt_unique_for_each_user` -> $db_salt_unique_for_each_user `salt_for_password -> $db_salt_for_password`.
  • , . : $salt_for_password = ALG1(«cat», $db_salt_unique_for_each_user).
  • $salt_for_password . . 12345, , . — ` salt = $db_salt_for_password`. : ` login = «alice»`.


Explicação : O servidor não notifica de nenhuma forma qual salt foi enviado - correto ou não. O resultado de seu uso ficará claro quando eles tentarem fazer o login com o nome de usuário e senha corretos.



  • O usuário recebe uma resposta do servidor. Sua página recarrega ou muda dinamicamente imediatamente.
  • O navegador do usuário envia cookies: login = alice , salt = "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg" .
  • O servidor verifica a solicitação do usuário para ver se há um cookie "salt". Encontra ela.
  • O navegador já possui salt para criptografar a senha.
  • O servidor envia um formulário de login com dois campos: login (já possui o valor alice ) + senha.
  • O usuário digita sua senha simples 12345 e clica no botão " Enviar ".
  • O navegador intercepta o evento onSubmit .
  • Calcula $ password_hashed = ALG2 ("12345", "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg").
  • Envia os dados "Alice" / $ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvHOUFoA


O servidor recebe uma solicitação de autenticação:



  • Dados de login + senha: "alice" / $ password_hashed
  • Vai para o banco de dados, obtém o valor ` password_hashed` -> $ db_password_hashed.
  • Compara $ db_password_hashed === $ password_hashed?
  • Os hashes correspondem, a autorização foi bem-sucedida.


Nota: No meu exemplo, o servidor compara os hashes diretamente. Mas você não pode armazenar strings no banco de dados que, na verdade, já sejam senhas. Eles podem ser roubados e usados ​​na forma de uma senha de login. Portanto, você precisa fazer hashes - não importa o quão estranho pareça. Isso significa que você precisa de um terceiro sal. Mas deve ser armazenado não no banco de dados, mas na variável de ambiente. No entanto, esses já são detalhes de implementação que deixei de fora para simplificar.



Enquanto isso, nosso hacker decide testar este estranho formulário de login:



  • O hacker digita login - alice , secret - dog e clica no botão " Enviar ".
  • O servidor recebe o pedido de um hacker e vê que uma palavra secreta foi enviada em vez de uma senha.
  • alice `salt_unique_for_each_user` -> $db_salt_unique_for_each_user `salt_for_password` -> $salt_for_password.


  • , , : $result_fake_salt = ALG1(«dog», $db_salt_unique_for_each_user). , .


O servidor envia o valor salt calculado de volta ao navegador do usuário. Os cabeçalhos indicam - `set cookie salt = $ result_fake_salt`. O login também é salvo: `configure o cookie login =" alice "`.



Explicação : Para ajudar o hacker com o trabalho duro, o servidor envia sal para ele. Mas é impossível determinar de fora se a palavra secreta estava correta ou não.



  • O hacker recebe a resposta do servidor. Sua página é recarregada ou muda dinamicamente imediatamente.
  • O navegador do hacker envia cookies: login = alice , salt = $ result_fake_salt .
  • O servidor verifica a solicitação do usuário para ver se há um cookie "salt". Encontra ela.
  • O navegador do hacker já possui sal para criptografar a senha.
  • : ( alice) + .
  • 12345 "".
  • onSubmit.
  • $password_hashed = ALG2(«12345», $result_fake_salt).
  • «alice»/$password_hashed.


O servidor recebe um pedido de autenticação - "alice" / $ password_hashed.

Vai para o banco de dados, obtém o valor `password_hashed` -> $ db_password_hashed.

Compara: $ password_hashed === $ db_password_hashed? Não.



Os hashes dessas senhas inicialmente idênticas não correspondem. Porque eles foram salgados de maneiras diferentes.



O hacker não desiste e vai cadastrar outro usuário no site.



Por acidente, ele digita a mesma palavra secreta que o usuário atrás da parede - gato .



O hacker obtém um salt de senha válido para a nova conta e tenta substituí-lo no script de hash.



Felizmente, a geração do salt de senha usou um segundo salt (`salt_unique_for_each_user`), que é gerado de uma nova maneira para cada usuário. Assim, usuários diferentes, mesmo com as mesmas senhas e - o mais importante - palavras secretas, terão sais diferentes. E o sal do usuário com a mesma palavra secreta não combinará com o sal de outro. E a correspondência de senhas também não será um problema.



Agora, com relação à complicação de senhas de força bruta em um dicionário. Se modificarmos o ALG2, que é comum tanto para o servidor quanto para o cliente, e torná-lo trabalhoso, isso complicará seriamente o ataque do hacker. Deixe-me lembrar a você que ALG2 é o processo de obtenção de um hash de senha que é enviado ao servidor. No servidor, este hash já foi calculado e armazenado no banco de dados:



  • o servidor irá realizar a operação ALG2 apenas uma vez ao escrever uma senha para o banco de dados ou alterar a senha para uma nova
  • o cliente só executará a operação ALG2 durante a autenticação (que não deve ser confundida com autorização). Digamos que o cliente cometeu um erro algumas vezes ao inserir a senha - tudo bem.
  • O hacker fará isso o tempo todo para cada senha, pelo que está de parabéns. É especialmente cínico que um esforço titânico seja despendido em senhas como 123/1234/12345.


Em máquinas fracas, a operação pode demorar muito mais do que em máquinas rápidas. isso pode ser um problema. Portanto, você não precisa complicar o algoritmo.



Vou terminar a descrição do conceito com um barril de alcatrão:



  • Se um usuário inserir acidentalmente uma palavra secreta incorretamente, ele estará em uma situação em que não poderá inserir usando sua senha. Você terá que redefinir a palavra secreta (no nosso caso, excluir os cookies) e enviar a solicitação novamente. Isso pode ser implementado de forma transparente pressionando um botão, mas antes disso o usuário deve adivinhar. Você pode forçar uma redefinição em 5 tentativas de login incorretas.
  • Dois usuários no mesmo computador terão que despejar constantemente o sal um do outro.
  • Dois computadores diferentes receberão o mesmo sal de senha
  • Se o salt for alterado no servidor por meio de um computador, o outro computador com o sal antigo não saberá que ele precisa ser alterado
  • Você pode roubar sal do seu computador e usá-lo para realizar um ataque muito rápido à sua conta, sabendo que a senha é muito simples.


... e uma colher de mel:



  • . , "cat" , "termorectal" — . , . , . , .
  • . `salt_for_password` , , , . .



All Articles