Com o aumento dos ataques cibernéticos, torna-se cada vez mais necessário adotar ferramentas e hábitos que dificultem o acesso de pessoas mal-intencionadas aos seus dados. Uma estratégia amplamente utilizada é a autenticação de dois fatores (2FA). Hoje, vamos explorar mais a fundo uma das diversas alternativas de segundo fator disponíveis no mercado: o TOTP.
O que é TOTP?
TOTP é a sigla para Time-Based One-Time Password Algorithm, um algoritmo utilizado para gerar senhas únicas baseadas no tempo. De acordo com a RFC 6238, ele é uma extensão do algoritmo HMAC-based One-Time Password (HOTP) descrito na RFC 4226, que também gera senhas seguras e únicas, mas é baseado em um contador, e não no tempo.
Como funciona o algoritmo?
Como mencionamos, o TOTP é uma extensão do HOTP, então, para entendê-lo melhor, vamos primeiro compreender como o HOTP funciona. O HOTP segue os passos abaixo, de forma simplificada, para gerar uma senha de uso único:
O servidor gera uma chave secreta para cada usuário.
O usuário armazena essa chave em um dispositivo.
A cada nova senha gerada, o contador é incrementado.
A fórmula pode ser definida da seguinte maneira:
HOTP(K, C) = Truncate(HMAC-SHA-1(K, C))
onde:
- K é a chave secreta.
- C é o contador, que deve estar sincronizado entre o servidor e o cliente.
- Truncate é uma função que converte o resultado do hash em um número amigável para o usuário.
Como discutimos anteriormente, a principal diferença entre o HOTP e o TOTP é o uso do tempo. A fórmula para o TOTP pode ser definida como:
TOTP = HMAC(K, T)
onde T representa o tempo. O tempo é calculado usando a seguinte fórmula:
T = floor(Current Unix time / step)
Mas o que é o step? Bem, não somos rápidos como o Relâmpago McQueen para gerar um código e inseri-lo no servidor no mesmo segundo. Portanto, é importante ter uma janela de tempo durante a qual o usuário possa gerar uma senha e inseri-la no servidor. Um valor comum para esse intervalo é de 30 segundos.
A função Truncate é um pouco mais complexa, mas o algoritmo está detalhado na seção 5.4 da RFC 4226. Em termos simplificados, os passos são:
- Obter um offset, que é definido pelos 4 últimos bits do último byte do hash gerado.
- Extrair 31 bits a partir do offset.
- Realizar uma operação de módulo para obter o valor amigável.
Exemplo de implementação em Elixir
defmodule TOTP do
@duration 30
@digit_count 6
import Bitwise
@spec generate_secret() :: binary()
def generate_secret do
:crypto.strong_rand_bytes(20) |> Base.encode32
end
@spec generate_totp(binary()) :: binary()
def generate_totp(key) do
{:ok, key} = Base.decode32(key)
key
|> hmac(calculate_T())
|> truncate()
end
@spec valid?(binary(), any()) :: boolean()
def valid?(secret, otp, since \\ nil),
do: otp == generate_totp(secret) and not has_been_used?(since)
#RFC4226 https://datatracker.ietf.org/doc/html/rfc4226#section-5.4
defp truncate(hash) do
# getting the last byte and masking it so we get a valid index
offset = :binary.last(hash) &&& 0x0F
# Extract 4 bytes starting from the calculated offset
<<_::binary-size(offset), extracted_value::integer-size(32), _rest::binary>> = hash
# Ensure the extracted value is positive
extracted_positive_value = extracted_value &&& 0x7FFFFFFF
# taking this number module to get the 06 digits
otp = rem(extracted_positive_value, :math.pow(10, @digit_count) |> round())
formatted_otp =
otp
|> Integer.to_string()
|> String.pad_leading(6, "0")
formatted_otp
end
defp hmac(key, t) do
:crypto.mac(:hmac, :sha, key, t)
end
defp calculate_T(), do: <<Integer.floor_div(now_unix(), @duration)::64>>
defp has_been_used?(nil), do: false
defp has_been_used?(since),
do: Integer.floor_div(now_unix(), @duration) <= Integer.floor_div(since, @duration)
defp now_unix(), do: DateTime.utc_now() |> DateTime.to_unix()
end
Nessa implementação, estamos seguindo os passos mencionados anteriormente. Temos 3 funções principais:
TOTP.generate_secret/0: Gera uma chave secreta para o usuário. Essa chave deve ser armazenada de forma segura pelo servidor para validar a solução gerada pelo usuário. Do lado do usuário, a chave deve ser importada em algum aplicativo no dispositivo, como o Google Authenticator. Para evitar que o usuário insira manualmente a chave, uma solução comum é permitir que ele escaneie um QR code.
TOTP.generate_totp/1: Recebe a chave do usuário e aplica o algoritmo que aprendemos anteriormente. Calcula o hash com base no tempo e formata o hash para um valor amigável.
TOTP.valid?/2: Recebe o código inserido pelo usuário e o confronta com o código gerado pelo servidor.
Conclusão
Podemos observar que o custo para realizar a autenticação de dois fatores utilizando TOTP é baixo comparado a outras formas como SMS, email e afins, que além de terem o custo para o envio da notificação, apresentam a possibilidade de interceptações durante o processo, além de exigir conexão com a internet ou sinal telefônico. Já o TOTP pode ser gerado pelo dispositivo, por exemplo em um celular, mesmo que ele não tenha nenhum desses requisitos.
Top comments (0)