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

Clone this wiki locally