Artigo original: How Time-based One-Time Passwords work and why you should use them in your app.

Escrito por: Prakash Sharma

Com o aumento das ameaças à segurança cibernética, tornou-se cada vez mais necessário atualizar os padrões de segurança de suas aplicações para a web. Você precisa se certificar de que as contas de seus usuários estejam seguras.

Hoje em dia, muitas aplicações da web on-line estão pedindo aos usuários para adicionar uma camada extra de segurança à sua conta. Elas fazem isso habilitando a autenticação de 2 fatores. Existem vários métodos de implementação da autenticação de 2 fatores – a autenticação TOTP (o algoritmo de senha única dependente do tempo – do inglês, Time-based One Time Password) é um deles.

Este artigo explica o que é, como e por que usar esse algoritmo. Antes de entender isso, no entanto, vamos dar uma breve olhada no que significa autenticação de dois fatores.

O que é a autenticação de dois fatores?

A autenticação de 2 fatores (ou autenticação de multifator) é apenas uma camada extra de segurança para a conta de um usuário. Isso significa que, depois de habilitar a autenticação de dois fatores, o usuário precisa passar por mais uma etapa para fazer login com êxito. Por exemplo, as etapas usuais para se fazer login em uma conta são:

ZqTllcloHTWpzWYDh-YsnOggoitxJSicGiVj
Sem a autenticação de dois fatores: pede-se aos usuários que insiram e-mail ou id de usuário e senha. Se as credenciais estiverem corretas, o usuário consegue fazer login.

Ao habilitar a autenticação de dois fatores, os passos agora passam a ser:

inJY5oemUFqSO2g5G6HvPpNt0I74XF0hlRKV
Com a autenticação de dois fatores: pede-se aos usuários que insiram e-mail ou id de usuário e senha. Se as credenciais estiverem corretas, pede-se ao usuário que insira um código específico (normalmente conhecido como senha de uso único). Se o código estiver correto, o usuário consegue fazer login.

Portanto, isso adiciona mais uma etapa ao processo de login. Esse método é mais seguro, porque um criminoso não pode acessar a conta do usuário, a menos que tenha acesso à senha normal do usuário e à senha única.

Atualmente, existem dois métodos amplamente utilizados para obter essa senha única:

  1. Com base em um SMS: nesse método, toda vez que o usuário faz login, ele recebe uma mensagem de texto para seu número de telefone registrado, que contém uma senha única.
  2. Com base na senha única dependente do tempo: nesse método, ao habilitar a autenticação de dois fatores, solicita-se ao usuário que faça o escaneamento de uma imagem com código QR usando uma aplicação específica no smartphone. A aplicação gera continuamente senhas de uso único dependentes do tempo para o usuário.

O método baseado em SMS não precisa de muita explicação. É fácil, mas tem seus problemas, como esperar pelo SMS em cada tentativa de login, problemas de segurança e assim por diante. O método baseado nas senhas de uso único dependentes do tempo está se tornando popular por causa de suas vantagens sobre o método baseado em SMS. Então, vamos entender como funciona o método das senhas de uso único dependentes do tempo.

Como o método das senhas de uso único dependentes do tempo funcionam

Antes de entender isso, vamos primeiro discutir quais problemas esse método resolverá para nós.

Ao usarmos esse método, estamos criando uma senha única no lado do usuário (em vez de no lado do servidor) através de uma aplicação para smartphone.

Isso significa que os usuários sempre têm acesso à sua senha única. Assim, isso impede que o servidor envie uma mensagem de texto toda vez que o usuário tenta fazer login.

Além disso, a senha gerada muda após um determinado intervalo de tempo, para que se comporte como uma senha única.

Agora, vamos entender o funcionamento do método e tentar implementar a solução acima nós mesmos. Nossos requisitos aqui são criar uma senha no lado do usuário e que essa senha deve continuar mudando.

A seguir, veremos um modo de implementar essa solução:

Quando o usuário habilita a autenticação de dois fatores:

1. O servidor de back-end cria uma chave secreta para aquele usuário específico.
2. O servidor, então, compartilha aquela chave secreta com a aplicação do trelefone do usuário
3. A aplicação do telefone inicia um contador.
4. A aplicação do telefone gera uma senha de uso único com aquela chave secreta e com o contador.
5. A aplicação do telefone muda o contador após determinado intervalo e gera novamente uma senha de uso único, o que a torna dinâmica.

Isso deve funcionar, mas há três problemas principais com isso:

  1. Como a aplicação gerará uma senha de uso único com a chave secreta e com o contador?
  2. Como o contador será atualizado? Como o servidor da web vai acompanhar o contador?
  3. Como o servidor compartilhará a chave secreta com a aplicação do telefone?

A solução do primeiro problema é definida pelo uso do algoritmo HOTP.

Entendendo o HOTP:

HOTP é a sigla em inglês para "HMAC-Based One-Time Password" (em português, senha de uso único com base em HMAC). Esse algoritmo foi publicado inicialmente como RFC4226 pela Internet Engineering Task Force (IETF). O HOTP define um algoritmo para criar uma senha de uso único a partir de uma chave secreta e de um contador.

É possível usar esse algoritmo em duas etapas:

  1. A primeira etapa é criar um hash HMAC a partir da chave secreta e do contador.
// Obtenha o hash HMAC (usando o algoritmo de criação de hash SHA-1) a partir da chave secreta e do contador
hmacHash = HMAC-SHA-1(chaveSecreta, contador);

2. O resultado desse código seria uma string de 20 bytes. Essa string longa não é adequada como uma senha de uso único. Então, precisamos de uma maneira de reduzi-la. O HOTP define uma maneira de fazer isso deixando-a no comprimento desejado.

// hmacHash[19] representa o 19º byte da string.
offset = hmacHash[19] & 0xf;
hashReduzido = (hmacHash[offset++] & 0x7f) << 24 | (hmacHash[offset++] & 0xff) << 16 | (hmacHash[offset++] & 0xff) << 8 | (hmacHashh[offset++] & 0xff);
finalOTP = (hashReduzido % (10 ^ numeroDeDigitosExigidosNaSenha));

Pode parecer assustador, mas não é. Nesse algoritmo, primeiramente, obtemos o offset, que são os últimos 4 bits de hmacHash[19]. Depois disso, concatenamos os bytes de hmacHash[offset] em hmacHash[offset+3] e armazenamos os últimos 31 bits de hashReduzido. Por fim, usando uma operação simples de módulo (%), obtemos a senha de uso único do tamanho desejado.

Isso, praticamente, define o algoritmo do HOTP. O documento RFA4226 explica por que essa é a maneira mais segura de se obter uma senha única desses dois valores.

Então, encontramos uma maneira de obter uma senha única usando uma chave secreta e um contador. O segundo problema é: como acompanhar o contador?

A solução para o segundo problema é encontrada na TOTP (abreviação para Time-Based One-Time Password, em inglês).

Entendendo a TOTP:

TOTP é a abreviação de "Time-Based One-Time Password". Ela foi publicada na RFC6238 pela IETF.

Uma TOTP usa o algoritmo do HOTP para obter a senha de uso único. A única diferença é que ele usa "tempo" no lugar do "contador", o que nos dá a solução para o nosso segundo problema.

Isso significa que, em vez de inicializar o contador e mantê-lo controlado, podemos usar o tempo como um contador no algoritmo do HOTP para obter a senha de uso único. Como um servidor e um telefone têm acesso ao tempo, nenhum deles precisa acompanhar o contador.

Além disso, para evitar o problema de fusos horários diferentes do servidor e do telefone, podemos usar um carimbo de data/hora Unix, que é independente dos fusos horários.

No entanto, o tempo Unix é definido em segundos e, por isso, muda a cada segundo. Isso significa que a senha gerada mudará a cada segundo, o que não é bom. Em vez disso, precisamos adicionar um intervalo significativo antes de alterar a senha. Por exemplo, a aplicação do Google Authenticator altera o código a cada 30 segundos.

contador = currentUnixTime / 30

Resolvemos, assim, o problema do contador. Agora, precisamos resolver nosso terceiro problema: compartilhar a chave secreta com a aplicação do telefone. Aqui, um código QR pode nos ajudar.

Usando um QR code

Embora possamos pedir aos usuários que digitem a chave secreta em sua aplicação de telefone diretamente, queremos tornar as chaves secretas bastante longas por razões de segurança. Pedir ao usuário para digitar uma string tão longa não seria uma experiência amigável.

Como a maioria dos smartphones é equipada com uma câmera, podemos usá-la e pedir ao usuário que escaneie um QR code para obter a chave secreta dele. Então, tudo o que precisamos fazer é converter a chave secreta no QR code e mostrá-la ao usuário.

Resolvemos os três problemas! Agora, você já sabe como funciona a TOTP. Vamos ver como implementá-la em uma aplicação.

Como implementar a TOTP

Existem algumas aplicações de telefone gratuitas disponíveis (como o Google Authenticator, Authy e outras) que podem gerar uma senha de uso único para o usuário. Portanto, na maioria dos casos, não é necessário criar sua própria aplicação de telefone.

Os pseudocódigos a seguir explicam uma maneira de implementar a autenticação de 2 fatores baseada em tempo em uma aplicação para a web.

Quando o usuário solicita habilitar a autenticação de dois fatores

// Gere uma chave secreta de comprimento 20.
secretKey = generateSecretKey(20);
// Salve a chave secreta no banco de dados para esse usuário específico.
saveUserSecretKey(userId, secretKey);
// converta a chave secreta em uma imagem de QR code.
qrCode = convertToQrCode(secretKey);
// envie a imagem de QR code como resposta 
response(qrCode);

É solicitado ao usuário que escaneie esse QR code. Quando a aplicação do telefone escaneia o QR code, ela recebe a chave secreta do usuário. Usando essa chave secreta, a hora atual do Unix e o algoritmo do HOTP, a aplicação de telefone gerará e exibirá a senha.

Pedimos ao usuário que digite o código gerado após escanear o QR code. Isso é necessário, porque queremos ter certeza de que o usuário escaneou com êxito a imagem e que a aplicação do telefone gerou o código com êxito.

O usuário digita o código exibido na aplicação.

// Obter a chave secreta do banco de dados.
secretKey = getSecretKeyOfUser(userId);
if (codeTypedByUser == getHOTP(secretKey, currentUnixTime / 30)) {
	enableTwoFactorAuthentication(userId);
}



Aqui, usamos o algoritmo DO HOTP no lado do servidor para obter a autenticação baseada na senha de uso único, na chave secreta e no tempo atual Unix. Se essa senha de uso único for a mesma digitada pelo usuário, podemos habilitar a autenticação de dois fatores para esse usuário.

Após cada operação de login, precisamos verificar se esse usuário específico tem a autenticação de 2 fatores habilitada. Se estiver habilitada, pedimos a senha única exibida na aplicação do telefone. Somente se esse código digitado estiver correto, o usuário será autenticado.

O usuário digita o código exibido na aplicação do telefone para fazer o login

// Obtém a chave secreta no banco de dados.
secretKey = getSecretKeyOfUser(userId);
if (codeTypedByUser == getHOTP(secretKey, currentUnixTime)) {
	signIn(userId);
}

O que acontece se o usuário perder o código?

Há algumas maneiras de ajudar o usuário a recuperar o código. Normalmente, quando eles ativam a autenticação de 2 fatores, podemos mostrar a chave secreta para eles junto com o QR code e pedir que salvem esse código em algum lugar com segurança.

Aplicações como o Google Authenticator permitem que você gere a senha inserindo diretamente a chave secreta. Se o usuário perder o código, ele pode inserir essa chave secreta salva com segurança na aplicação do telefone para gerar a senha de uso único novamente.

Conclusão

A autenticação de dois fatores está ganhando popularidade. Muitas aplicações da web já estão implementando-a para ter mais segurança.

Ao contrário do método baseado em SMS, o método da TOTP também não requer muito esforço extra. Portanto, vale a pena implementar esse recurso para qualquer aplicação.