Skip to content
mobius1qwe edited this page Nov 5, 2026 · 1 revision

Home > Autenticação > Senhas, revogação e TOTP 🚧

Guardar senhas, revogar tokens e o segundo fator (TOTP)

Important

Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.

Vale para a 2.0 · Delphi XE em diante e Lazarus/FPC 3.2 em diante · Nível: intermediário

Em uma frase: o RAL guarda senhas com Argon2id, bcrypt, scrypt ou PBKDF2 (em Pascal, sem DLL), revoga tokens JWT em todos os servidores, troca a chave do JWT sem derrubar ninguém e confere os códigos de 6 dígitos dos aplicativos autenticadores.

Você vai precisar: o pacote RALCryptoPasswordPackage para as senhas; o resto está no PascalRAL (TOTP) e no RALPluginAuthPackage (JWT).

Senhas

Nunca guarde a senha, nem um MD5/SHA dela: guarde o hash de senha.

uses RALPassword;

Hash := RALPasswordHash(Senha);                 // Argon2id, parâmetros da OWASP
if RALPasswordVerify(SenhaDigitada, Hash) then  // reconhece qualquer formato abaixo
  ...
if RALPasswordNeedsRehash(Hash) then            // fraco ou de outro algoritmo
  Hash := RALPasswordHash(SenhaDigitada);       // troque no login, com a senha certa em mãos
algoritmo formato guardado padrão
Argon2id (padrão) $argon2id$v=19$m=19456,t=2,p=1$sal$hash 19 MiB, 2 passadas
bcrypt $2b$10$... (o mesmo do htpasswd -B e do PHP) custo 10
scrypt $scrypt$ln=17,r=8,p=1$sal$hash N = 2^17
PBKDF2-SHA256 / SHA512 $pbkdf2-sha256$i=600000,l=32$sal$hash 600 000 / 210 000 iterações

Para outro algoritmo ou custo, use TRALPasswordOptions (ou o componente TRALPasswordHasher, com Options no Object Inspector). Verify lê hashes de outras ferramentas no mesmo formato (Argon2 de argon2-cffi, bcrypt do PHP/Python/htpasswd).

Usuários e senhas para o Basic

Lista := TRALPasswordList.Create(Self);
Lista.FileName := 'usuarios.txt';      // linhas usuario:hash (o formato do htpasswd)
Lista.OnRehash := SalvarNovoHash;      // um login com hash fraco ganha um novo
BasicAuth.Passwords := Lista;          // sem OnValidate, o Basic confere aqui

Lista.SetPassword('ana', 'nova senha') cria ou troca; Lista.Save grava o arquivo. Usuário inexistente leva o mesmo tempo que senha errada: o tempo não entrega quem existe.

JWT: revogar e trocar a chave

JWT.RevocationStore := Armazenamento;  // TRALFileStore ou outro compartilhado entre servidores
JWT.Revoke(Token);                     // este token, até ele expirar
JWT.RevokeAllFor('ana');               // todos os tokens da ana emitidos até agora (logout geral)

Com RevocationStore, todo token ganha jti e iat. Um token revogado é 401 em todos os servidores que usam o mesmo armazenamento; o TRALServerOAuth2 também lê e grava ali.

Troca de chave sem derrubar ninguém, por Keys:

  1. JWT.Keys.AddSecret('2026-11', 'nova-chave') (ou AddPrivateKey com um PEM RSA/EC): os novos tokens saem com kid = 2026-11; os antigos continuam valendo.
  2. Depois que os tokens da chave antiga expiraram, Retired := True nela.

NotBefore publica uma chave antes de ela assinar. Com chaves RS/ES, PublishJWKS responde GET /.well-known/jwks.json com as públicas, para outros servidores conferirem os tokens (o TRALServerOAuth2 em modo ovJWKS, por exemplo).

Segundo fator: TOTP

uses RALOTP;

Totp := TRALTOTP.Create;
Totp.Issuer := 'Minha Empresa';
Segredo := Totp.NewSecret;                         // guarde junto do usuário
QR := Totp.URI('ana@empresa.com', Segredo);        // mostre como QR code
...
Totp.Store := Armazenamento;                       // o mesmo código não vale duas vezes
if Totp.Verify(Segredo, CodigoDigitado, 'ana') then
  ...

Funciona com Google Authenticator, Microsoft Authenticator, Authy e afins (SHA-1, 6 dígitos, 30 s). Window (padrão 1) aceita o código anterior e o próximo, para relógios um pouco fora. Num login JWT, peça o código no OnGetToken depois da senha.

Erros comuns

sintoma causa
login lento é o custo do hash, de propósito (~50 ms no padrão); não baixe abaixo da OWASP
Verify sempre False com hash do PHP confira se o texto chegou inteiro ($2y$ é aceito)
token revogado ainda entra em outro servidor os servidores não usam o mesmo RevocationStore
depois de aposentar a chave, todos deslogaram Retired antes de os tokens antigos expirarem
código TOTP sempre inválido relógio do servidor errado (use NTP) ou segredo digitado com letras trocadas

Veja também

Começar

Servidor

MQTT 🚧

Cliente

Motores

Avançado

Ajuda

🚧 = em desenvolvimento, pode mudar

Clone this wiki locally