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:

  1. 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).
  2. 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: publicKeycreate()

    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()

  3. 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 propriedade PublicKeyCredential.response contém as informações de atestado).create()
  4. O aplicativo Web encaminha o PublicKeyCredential para o servidor, novamente usando um mecanismo apropriado.
  5. 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:
    1. Verificando se o desafio é o mesmo que o desafio que foi enviado.
    2. Garantindo que a origem era a origem esperada.
    3. 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:

  1. Não requer informações do usuário ou da terceira parte confiável
  2. 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:

  1. 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.
  2. 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: publicKeyget()

    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()

  3. 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 propriedade PublicKeyCredential.response contém as informações de declaração).get()
  4. 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:
    1. Usando a chave pública que foi armazenada durante a solicitação de registro para validar a assinatura pelo autenticador.
    2. Garantir que o desafio assinado pelo autenticador corresponda ao desafio gerado pelo servidor.
    3. Verificar se o ID da Terceira Parte Confiável é o esperado para este serviço.
  5. 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:

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>

  1. 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 *" />
    
  2. 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()

AuthenticatorResponse

A interface base para AuthenticatorAttestationResponse e AuthenticatorAssertionResponse.

PublicKeyCredential

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

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 trabalhotelemóvel
Cromar
Borda
Firefox
Ópera
Safári
Chrome Android
Firefox para Android
Opera Android
Safari no iOS
Samsung Internet
WebView Android
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.


Artigo Original