Migrar para as dicas de cliente do user agent

Estratégias para migrar seu site da dependência da string do user agent para as novas dicas de cliente HTTP do user agent.

A string User-Agent é uma superfície de impressão digital passiva importante nos navegadores, além de ser difícil de processar. No entanto, há vários motivos válidos para coletar e processar dados de user agent. Portanto, o que é necessário é um caminho para uma solução melhor. As dicas do cliente do user-agent oferecem uma maneira explícita de declarar sua necessidade de dados de user-agent e métodos para retornar os dados em um formato fácil de usar.

Neste artigo, você vai aprender a auditar seu acesso aos dados do user agent e migrar o uso da string do user agent para as dicas de cliente do user agent.

Auditoria da coleta e do uso de dados de user-agent

Como acontece com qualquer forma de coleta de dados, é preciso entender sempre por que você está coletando esses dados. A primeira etapa, independentemente de você realizar ou não qualquer ação, é entender onde e por que você está usando dados do user agent.

Se você não souber se ou onde os dados do user agent estão sendo usados, pesquise seu código de front-end para usar navigator.userAgent e seu código de back-end para usar o cabeçalho HTTP User-Agent. Também verifique se o código front-end usa recursos descontinuados, como navigator.platform e navigator.appVersion.

De um ponto de vista funcional, pense em qualquer lugar do código em que você está gravando ou processando:

  • Nome ou versão do navegador
  • Nome ou versão do sistema operacional
  • Marca ou modelo do dispositivo
  • Tipo, arquitetura ou bitness da CPU (por exemplo, 64 bits)

Também é provável que você esteja usando uma biblioteca ou um serviço de terceiros para processar o user agent. Nesse caso, verifique se eles estão sendo atualizados para oferecer suporte às dicas de cliente do user agent.

Você está usando apenas dados básicos do user-agent?

O conjunto padrão de dicas de cliente HTTP do user agent inclui:

  • Sec-CH-UA: nome do navegador e versão principal/significativa
  • Sec-CH-UA-Mobile: valor booleano que indica um dispositivo móvel.
  • Sec-CH-UA-Platform: nome do sistema operacional
    • Isso foi atualizado na especificação e será refletido no Chrome e em outros navegadores baseados no Chromium em breve.

A versão reduzida da string do user agent proposta também vai manter essas informações básicas de forma compatível com versões anteriores. Por exemplo, em vez de Chrome/90.0.4430.85, a string incluiria Chrome/90.0.0.0.

Se você estiver verificando apenas a string do user-agent para nome do navegador, versão principal ou sistema operacional, o código continuará funcionando, mas é provável que você receba avisos de descontinuação.

Embora seja possível e recomendável migrar para as dicas de cliente do user agent, você pode ter restrições de código legado ou de recursos que impedem isso. A redução de informações na string do user agent dessa maneira compatível com versões anteriores tem como objetivo garantir que o código atual receba informações menos detalhadas, mas ainda mantenha a funcionalidade básica.

Estratégia: API JavaScript sob demanda do lado do cliente

Se você estiver usando navigator.userAgent, faça a transição para preferir navigator.userAgentData antes de voltar a analisar a string do user-agent.

if (navigator.userAgentData) {
  // use new hints
} else {
  // fall back to user-agent string parsing
}

Se você estiver verificando dispositivos móveis ou computadores, use o valor booleano mobile:

const isMobile = navigator.userAgentData.mobile;

userAgentData.brands é uma matriz de objetos com propriedades brand e version, em que o navegador pode listar a compatibilidade com essas marcas. É possível acessá-lo diretamente como uma matriz ou usar uma chamada some() para verificar se uma entrada específica está presente:

function isCompatible(item) {
  // In real life you most likely have more complex rules here
  return ['Chromium', 'Google Chrome', 'NewBrowser'].includes(item.brand);
}
if (navigator.userAgentData.brands.some(isCompatible)) {
  // browser reports as compatible
}

Se você precisar de um dos valores de user agent mais detalhados e de alta entropia, será preciso especificá-lo e verificar o resultado no Promise retornado:

navigator.userAgentData.getHighEntropyValues(['model'])
  .then(ua => {
    // requested hints available as attributes
    const model = ua.model
  });

Você também pode usar essa estratégia se quiser mudar do processamento do lado do servidor para o do lado do cliente. A API JavaScript não exige acesso a cabeçalhos de solicitação HTTP. Portanto, os valores do user agent podem ser solicitados a qualquer momento.

Estratégia: cabeçalho estático do lado do servidor

Se você estiver usando o cabeçalho de solicitação User-Agent no servidor e suas necessidades para esses dados forem relativamente consistentes em todo o site, poderá especificar as dicas de cliente desejadas como um conjunto estático nas respostas. Essa é uma abordagem relativamente simples, já que geralmente você só precisa configurá-la em um local. Por exemplo, pode estar na configuração do servidor da Web, se você já adicionou cabeçalhos, na configuração de hospedagem ou na configuração de nível superior do framework ou da plataforma que você usa para o site.

Considere essa estratégia se você estiver transformando ou personalizando as respostas fornecidas com base nos dados do user-agent.

Navegadores ou outros clientes podem fornecer dicas padrão diferentes. Por isso, é uma boa prática especificar tudo o que você precisa, mesmo que seja fornecido por padrão.

Por exemplo, os padrões atuais do Chrome seriam representados como:

⬇️ Cabeçalhos de resposta

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

Se você também quiser receber o modelo do dispositivo nas respostas, envie:

⬇️ Cabeçalhos de resposta

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA

Ao processar isso no servidor, primeiro verifique se o cabeçalho Sec-CH-UA desejado foi enviado e, em seguida, use a análise do cabeçalho User-Agent se ele não estiver disponível.

Estratégia: delegar dicas para solicitações entre origens

Se você estiver solicitando subrecursos de origem cruzada ou entre sites que exigem que as dicas de cliente do user agent sejam enviadas nas solicitações, será necessário especificar explicitamente as dicas desejadas usando uma política de permissões.

Por exemplo, digamos que https://blog.site hospede recursos em https://cdn.site, que pode retornar recursos otimizados para um dispositivo específico. O https://blog.site pode solicitar a sugestão Sec-CH-UA-Model, mas precisa delegar isso explicitamente para https://cdn.site usando o cabeçalho Permissions-Policy. A lista de dicas controladas por políticas está disponível no rascunho da infraestrutura de Client Hints.

⬇️ Resposta de blog.site delegando a dica

Accept-CH: Sec-CH-UA-Model
Permissions-Policy: ch-ua-model=(self "https://cdn.site")

⬆️ A solicitação para subrecursos em cdn.site inclui a sugestão delegada

Sec-CH-UA-Model: "Pixel 5"

É possível especificar várias dicas para várias origens, e não apenas do intervalo ch-ua:

⬇️ Resposta de blog.site delegando várias dicas para várias origens

Accept-CH: Sec-CH-UA-Model, DPR
Permissions-Policy: ch-ua-model=(self "https://cdn.site"),
                    ch-dpr=(self "https://cdn.site" "https://img.site")

Estratégia: delegar dicas para iframes

Os iframes entre origens funcionam de maneira semelhante aos recursos entre origens, mas você especifica as dicas que gostaria de delegar no atributo allow.

⬇️ Resposta de blog.site

Accept-CH: Sec-CH-UA-Model

↪️ HTML para blog.site

<iframe src="https://widget.site" allow="ch-ua-model"></iframe>

⬆️ Solicitação para widget.site

Sec-CH-UA-Model: "Pixel 5"

O atributo allow no iframe substitui qualquer cabeçalho Accept-CH que o widget.site enviar. Portanto, verifique se você especificou tudo o que o site com iframe precisa.

Estratégia: dicas dinâmicas do lado do servidor

Se você tem partes específicas da jornada do usuário em que precisa de uma seleção maior de dicas do que no restante do site, pode solicitar essas dicas sob demanda, e não estaticamente em todo o site. Isso é mais complexo de gerenciar, mas se você já definiu cabeçalhos diferentes por rota, pode ser viável.

O importante a lembrar aqui é que cada instância do cabeçalho Accept-CH vai substituir o conjunto atual. Portanto, se você estiver definindo o cabeçalho de forma dinâmica, cada página precisará solicitar o conjunto completo de dicas necessárias.

Por exemplo, você pode ter uma seção no seu site em que quer fornecer ícones e controles que correspondam ao sistema operacional do usuário. Para isso, talvez seja necessário usar Sec-CH-UA-Platform-Version para fornecer subrecursos adequados.

⬇️ Cabeçalhos de resposta para /blog

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA

⬇️ Cabeçalhos de resposta para /app

Accept-CH: Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA

Estratégia: dicas do lado do servidor necessárias na primeira solicitação

Pode haver casos em que você vai precisar de mais do que o conjunto padrão de dicas na primeira solicitação. No entanto, isso é raro, então verifique se você analisou o raciocínio.

A primeira solicitação significa a primeira solicitação de nível superior para essa origem enviada nessa sessão de navegação. O conjunto padrão de dicas inclui o nome do navegador com a versão principal, a plataforma e o indicador de dispositivo móvel. A pergunta a ser feita aqui é: você precisa de dados estendidos no carregamento inicial da página?

Para mais dicas sobre a primeira solicitação, há duas opções. Primeiro, você pode usar o cabeçalho Critical-CH. Ele tem o mesmo formato de Accept-CH, mas informa ao navegador que ele precisa tentar a solicitação imediatamente se a primeira foi enviada sem a dica crítica.

⬆️ Solicitação inicial

[With default headers]

⬇️ Cabeçalhos de resposta

Accept-CH: Sec-CH-UA-Model
Critical-CH: Sec-CH-UA-Model

🔃 O navegador tenta novamente a solicitação inicial com o cabeçalho extra

[With default headers + …]
Sec-CH-UA-Model: Pixel 5

Isso vai gerar sobrecarga da nova tentativa na primeira solicitação, mas o custo de implementação é relativamente baixo. Envie o cabeçalho extra, e o navegador fará o resto.

Para situações em que você realmente precisa de dicas adicionais na primeira carga da página, a proposta de confiabilidade de dicas do cliente está definindo uma rota para especificar dicas nas configurações de nível de conexão. Isso usa a extensão Application-Layer Protocol Settings(ALPS) para TLS 1.3 para permitir essa transmissão antecipada de dicas em conexões HTTP/2 e HTTP/3. Esse estágio ainda está em fase inicial, mas se você gerencia ativamente suas próprias configurações de TLS e conexão, esse é o momento ideal para contribuir.

Estratégia: suporte legado

Talvez você tenha um código legado ou de terceiros no seu site que dependa de navigator.userAgent, incluindo partes da string do user agent que serão reduzidas. A longo prazo, planeje migrar para as chamadas navigator.userAgentData equivalentes, mas há uma solução provisória.

O retrofill do UA-CH é uma pequena biblioteca que permite substituir navigator.userAgent por uma nova string criada com base nos valores navigator.userAgentData solicitados.

Por exemplo, este código vai gerar uma string de user agent que também inclui a dica "modelo":

import { overrideUserAgentUsingClientHints } from './uach-retrofill.js';
overrideUserAgentUsingClientHints(['model'])
  .then(() => { console.log(navigator.userAgent); });

A string resultante mostraria o modelo Pixel 5, mas ainda mostraria o 92.0.0.0 reduzido, já que a dica uaFullVersion não foi solicitada:

Mozilla/5.0 (Linux; Android 10.0; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.0.0 Mobile Safari/537.36

Mais suporte

Se essas estratégias não atenderem ao seu caso de uso, inicie uma discussão no repo privacy-sandbox-dev-support e podemos analisar o problema juntos.

Foto de Ricardo Rocha no Unsplash (links em inglês)