API de autenticação da Web
API de autenticação da Web
Contexto seguro: Esse recurso está disponível apenas em contextos seguros (HTTPS), em alguns ou todos os navegadores compatíveis.
A API de Autenticação da Web (WebAuthn) é uma extensão da API de Gerenciamento de Credenciais que permite autenticação forte com criptografia de chave pública, permitindo autenticação sem senha e autenticação multifator segura (MFA) sem textos SMS.
Conceitos e uso do WebAuthn
O WebAuthn usa criptografia assimétrica (chave pública) em vez de senhas ou textos SMS para registro, autenticação e autenticação multifator com sites. Isso tem alguns benefícios:
- Proteção contra phishing: Um invasor que cria um site de login falso não pode fazer login como usuário porque a assinatura muda com a origem do site.
- Impacto reduzido de violações de dados: Os desenvolvedores não precisam fazer hash da chave pública e, se um invasor obtiver acesso à chave pública usada para verificar a autenticação, ele não poderá autenticar porque precisa da chave privada.
- Invulnerável a ataques de senha: Alguns usuários podem reutilizar senhas e um invasor pode obter a senha do usuário para outro site (por exemplo, por meio de uma violação de dados). Além disso, as senhas de texto são muito mais fáceis de usar força bruta do que uma assinatura digital.
Muitos sites já possuem páginas que permitem aos usuários registrar novas contas ou entrar em uma conta existente, e o WebAuthn atua como um substituto ou aprimoramento para a parte de autenticação do sistema. Ele estende a API de Gerenciamento de Credenciais, abstraindo a comunicação entre o agente do usuário e um autenticador e fornecendo a seguinte nova funcionalidade:
- Quando
navigator.credentials.create()
é usado com a opção, o agente do usuário cria novas credenciais por meio de um autenticador — seja para registrar uma nova conta ou para associar um novo par de chaves assimétricas a uma conta existente.publicKey
- Ao registrar uma nova conta, essas credenciais são armazenadas em um servidor (também conhecido como serviço ou terceira parte confiável) e podem ser usadas posteriormente para fazer logon de um usuário.
- O par de chaves assimétricas é armazenado no autenticador, que pode ser usado para autenticar um usuário com uma terceira parte confiável, por exemplo, durante a MFA. O autenticador pode ser incorporado ao agente do usuário, a um sistema operacional, como o Windows Hello, ou pode ser um token físico, como uma chave de segurança USB ou Bluetooth.
- Quando
navigator.credentials.get()
é usado com a opção, o agente do usuário usa um conjunto existente de credenciais para autenticar uma terceira parte confiável (como o logon principal ou para fornecer um fator adicional durante a MFA, conforme descrito acima).publicKey
Em suas formas mais básicas, ambos recebem um número aleatório muito grande chamado “desafio” do servidor e retornam o desafio assinado pela chave privada de volta ao servidor. Isso prova ao servidor que um usuário tem a chave privada necessária para autenticação sem revelar segredos pela rede.create()
get()
Nota: O “desafio” deve ser um buffer de informações aleatórias de pelo menos 16 bytes de tamanho.
Criando um par de chaves e registrando um usuário
Para ilustrar como o processo de criação de credenciais funciona, vamos descrever o fluxo típico que ocorre quando um usuário deseja registrar uma credencial para uma terceira parte confiável:
- O servidor de terceira parte confiável envia informações de usuário e terceira parte confiável para o aplicativo Web que lida com o processo de registro, juntamente com o “desafio”, usando um mecanismo seguro apropriado (por exemplo, XMLHttpRequest ou Fetch).
-
O aplicativo Web inicia a geração de uma nova credencial por meio do autenticador, em nome da terceira parte confiável, por meio de uma chamada
navigator.credentials.create().
Essa chamada recebe uma opção especificando os recursos do dispositivo, por exemplo, se o dispositivo fornece sua própria autenticação de usuário (por exemplo, com biometria). Uma chamada típica pode ter a seguinte aparência:publicKey
create()
let credential = await navigator.credentials.create({ publicKey: { challenge: new Uint8Array([117, 61, 252, 231, 191, 241, ...]), rp: { id: "acme.com", name: "ACME Corporation" }, user: { id: new Uint8Array([79, 252, 83, 72, 214, 7, 89, 26]), name: "jamiedoe", displayName: "Jamie Doe" }, pubKeyCredParams: [ {type: "public-key", alg: -7} ] } });
Os parâmetros da chamada são passados para o autenticador, juntamente com um hash SHA-256 que é assinado para garantir que ele não seja adulterado.
create()
- Depois que o autenticador obtém o consentimento do usuário, ele gera um par de chaves e retorna a chave pública e o atestado assinado opcional para o aplicativo Web. Isso é fornecido quando a
Promessa
retornada pela chamada é cumprida, na forma de uma instância de objeto PublicKeyCredential (a propriedadePublicKeyCredential.response
contém as informações de atestado).create()
- O aplicativo Web encaminha o
PublicKeyCredential
para o servidor, novamente usando um mecanismo apropriado. - O servidor armazena a chave pública, juntamente com a identidade do usuário, para lembrar a credencial para autenticações futuras. Durante esse processo, ele realiza uma série de verificações para garantir que o cadastro foi concluído e não adulterado. Estes incluem:
- Verificando se o desafio é o mesmo que o desafio que foi enviado.
- Garantindo que a origem era a origem esperada.
- Validar se a assinatura e o atestado estão usando a cadeia de certificados correta para o modelo específico do autenticador usado para gerar o par de chave em primeiro lugar.
Aviso: O atestado fornece uma maneira de uma terceira parte confiável determinar a procedência de um autenticador. As partes confiáveis não devem tentar manter listas de permissões de autenticadores.
Autenticando um usuário
Depois que um usuário se registra no WebAuthn, ele pode autenticar (ou seja, fazer login) com o serviço. O fluxo de autenticação é semelhante ao fluxo de registro, sendo as principais diferenças que a autenticação:
- Não requer informações do usuário ou da terceira parte confiável
- Cria uma declaração usando o par de chaves gerado anteriormente para o serviço, em vez do par de chaves do autenticador.
Um fluxo de autenticação típico é o seguinte:
- A terceira parte confiável gera um “desafio” e o envia ao agente do usuário usando um mecanismo seguro apropriado, juntamente com uma lista de credenciais de terceira parte confiável e de usuário. Ele também pode indicar onde procurar a credencial, por exemplo, em um autenticador interno local ou em um externo por USB, BLE, etc.
-
O navegador pede ao autenticador para assinar o desafio por meio de uma chamada
navigator.credentials.get(),
que é passada as credenciais em uma opção. Uma chamada típica pode ter a seguinte aparência:publicKey
get()
let credential = await navigator.credentials.get({ publicKey: { challenge: new Uint8Array([139, 66, 181, 87, 7, 203, ...]), rpId: "acme.com", allowCredentials: [{ type: "public-key", id: new Uint8Array([64, 66, 25, 78, 168, 226, 174, ...]) }], userVerification: "required", } });
Os parâmetros da chamada são passados para o autenticador para manipular a autenticação.
get()
- Se o autenticador contiver uma das credenciais fornecidas e conseguir assinar com êxito o desafio, ele retornará uma declaração assinada ao aplicativo Web após receber o consentimento do usuário. Isso é fornecido quando a
Promise
retornada pela chamada é cumprida, na forma de uma instância de objeto PublicKeyCredential (a propriedadePublicKeyCredential.response
contém as informações de declaração).get()
- O aplicativo Web encaminha a declaração assinada para o servidor de terceira parte confiável para que a terceira parte confiável valide. As verificações de validação incluem:
- Usando a chave pública que foi armazenada durante a solicitação de registro para validar a assinatura pelo autenticador.
- Garantir que o desafio assinado pelo autenticador corresponda ao desafio gerado pelo servidor.
- Verificar se o ID da Terceira Parte Confiável é o esperado para este serviço.
- Uma vez verificado pelo servidor, o fluxo de autenticação é considerado bem-sucedido.
Controlando o acesso à API
A disponibilidade do WebAuthn pode ser controlada usando uma Política de Permissões, especificando duas diretivas em particular:
publickey-credentials-create
: Controla a disponibilidade denavigator.credentials.create()
com a opção.publicKey
publickey-credentials-get
: Controla a disponibilidade denavigator.credentials.get()
com a opção.publicKey
Ambas as diretivas têm um valor de allowlist padrão de , o que significa que, por padrão, esses métodos podem ser usados em contextos de documento de nível superior. Além disso, pode ser usado em contextos de navegação aninhados carregados da mesma origem do documento mais alto; por outro lado, não pode ser usado em <iframe>
s."self"
get()
create()
Nota: Quando uma política proíbe o uso desses métodos, as promessas
retornadas por eles serão rejeitadas com um DOMException
.SecurityError
Controle de acesso básico
Se você deseja permitir o acesso apenas a um subdomínio específico, você pode fornecê-lo assim:
Permissions-Policy: publickey-credentials-get=("https://subdomain.example.com")
Permissions-Policy: publickey-credentials-create=("https://subdomain.example.com")
Permitindo chamadas get()
incorporadas em um <iframe>
Se você deseja autenticar com em um , há algumas etapas a serem seguidas:get()
<iframe>
-
O site que incorpora o site de terceira parte confiável deve fornecer permissão por meio de um atributo:
allow
<iframe src="https://auth.provider.com" allow="publickey-credentials-get *" />
-
O site de terceira parte confiável deve fornecer permissão para o acesso acima por meio de um cabeçalho:
Permissions-Policy
Permissions-Policy: publickey-credentials-get=*
Ou para permitir que apenas uma URL específica incorpore o site de terceira parte confiável em um :
<iframe>
Permissions-Policy: publickey-credentials-get=("https://subdomain.example.com")
Interfaces
AuthenticatorAssertionResponse
Fornece prova a um serviço de que um autenticador tem o par de chaves necessário para manipular com êxito uma solicitação de autenticação iniciada por uma chamada CredentialsContainer.get().
Disponível na propriedade response da instância PublicKeyCredential
obtida quando a promessa
é cumprida.get()
AuthenticatorAttestationResponse
O resultado de um registro de credencial WebAuthn (ou seja, uma chamada CredentialsContainer.create().
Ele contém informações sobre a credencial que o servidor precisa para executar asserções WebAuthn, como sua ID de credencial e chave pública. Disponível na propriedade response da instância PublicKeyCredential
obtida quando a promessa
é cumprida.create()
A interface base para AuthenticatorAttestationResponse
e AuthenticatorAssertionResponse
.
Fornece informações sobre um par de chaves públicas/privadas, que é uma credencial para fazer login em um serviço usando um par de chaves assimétricas não phishable e resistente à violação de dados em vez de uma senha. Obtido quando a Promessa
retornada por meio de uma chamada create() ou get()
é cumprida.
Extensões para outras interfaces
CredentialsContainer.create()
, a opçãopublicKey
Chamar com uma opção inicia a criação de novas credenciais de chave assimétrica por meio de um autenticador, conforme explicado acima.create()
publicKey
CredentialsContainer.get()
, a opçãopublicKey
Chamar com uma opção instrui o agente do usuário a usar um conjunto existente de credenciais para autenticar uma terceira parte confiável.get()
publicKey
Exemplos
Sites de demonstração
- Site Mozilla Demo e seu código fonte.
- Site de demonstração do Google e seu código-fonte.
- WebAuthn.io site de demonstração e seu código-fonte.
- github.com/webauthn-open-source e seu código-fonte do cliente e o código-fonte do servidor
Sites de demonstração
Exemplo de uso
Nota: Por motivos de segurança, as chamadas da API de Autenticação da Web (create
() e get())
são canceladas se a janela do navegador perder o foco enquanto a chamada estiver pendente.
// sample arguments for registration
const createCredentialDefaultArgs = {
publicKey: {
// Relying Party (a.k.a. - Service):
rp: {
name: "Acme",
},
// User:
user: {
id: new Uint8Array(16),
name: "carina.p.anand@example.com",
displayName: "Carina P. Anand",
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7,
},
],
attestation: "direct",
timeout: 60000,
challenge: new Uint8Array([
// must be a cryptographically random number sent from a server
0x8c, 0x0a, 0x26, 0xff, 0x22, 0x91, 0xc1, 0xe9, 0xb9, 0x4e, 0x2e, 0x17, 0x1a,
0x98, 0x6a, 0x73, 0x71, 0x9d, 0x43, 0x48, 0xd5, 0xa7, 0x6a, 0x15, 0x7e, 0x38,
0x94, 0x52, 0x77, 0x97, 0x0f, 0xef,
]).buffer,
},
};
// sample arguments for login
const getCredentialDefaultArgs = {
publicKey: {
timeout: 60000,
// allowCredentials: [newCredential] // see below
challenge: new Uint8Array([
// must be a cryptographically random number sent from a server
0x79, 0x50, 0x68, 0x71, 0xda, 0xee, 0xee, 0xb9, 0x94, 0xc3, 0xc2, 0x15, 0x67,
0x65, 0x26, 0x22, 0xe3, 0xf3, 0xab, 0x3b, 0x78, 0x2e, 0xd5, 0x6f, 0x81, 0x26,
0xe2, 0xa6, 0x01, 0x7d, 0x74, 0x50,
]).buffer,
},
};
// register / create a new credential
navigator.credentials
.create(createCredentialDefaultArgs)
.then((cred) => {
console.log("NEW CREDENTIAL", cred);
// normally the credential IDs available for an account would come from a server
// but we can just copy them from above…
const idList = [
{
id: cred.rawId,
transports: ["usb", "nfc", "ble"],
type: "public-key",
},
];
getCredentialDefaultArgs.publicKey.allowCredentials = idList;
return navigator.credentials.get(getCredentialDefaultArgs);
})
.then((assertion) => {
console.log("ASSERTION", assertion);
})
.catch((err) => {
console.log("ERROR", err);
});
Especificações
Especificação |
---|
[Autenticação da Web: uma API para acessar credenciais de chave pública - Nível 3 |
# iface-pkcredencial](https://w3c.github.io/webauthn/#iface-pkcredential) |
Compatibilidade do navegador
Relatar problemas com esses dados de compatibilidade no GitHub
área de trabalho | telemóvel | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
PublicKeyCredential | |||||||||||
authenticatorAttachment | |||||||||||
getClientExtensionResults | |||||||||||
isConditionalMediationAvailable() método estático | |||||||||||
isUserVerifyingPlatformAuthenticatorAvailable() método estático | |||||||||||
rawId | |||||||||||
response |
Consulte as notas de implementação.
Tem mais informações de compatibilidade.