1 pontos por GN⁺ 3 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • A caixa de entrada de autenticação por e-mail de 6 dígitos do sistema de login do governo suíço AGOV não conseguia processar a digitação de números em teclados franceses AZERTY, impedindo o próprio cadastro da conta
  • O JavaScript da caixa de entrada interceptava as teclas e armazenava o código de autenticação em uma variável separada, mas ignorava a tecla Shift, na prática bloqueando o layout AZERTY, que exige Shift para digitar números
  • O mesmo erro ocorria em várias combinações de Firefox·Chromium·Chrome·Safari com Linux·macOS, mas funcionava normalmente em teclados QWERTY, restringindo a causa a um problema de layout de teclado, não de navegador ou sistema operacional
  • O suporte oficial também exigia passar pela mesma autenticação por e-mail, e não havia alternativas como e-mail ou telefone; assim, usuários que não conseguiam fazer login também não conseguiam reportar o bug de login
  • Usar lógica complexa de tratamento de teclas em vez de campos de entrada nativos, e amarrar o produto e os canais de suporte ao mesmo fluxo de autenticação, derruba ao mesmo tempo a acessibilidade e a resposta a incidentes; por isso, são necessários entrada simples baseada no DOM e canais de suporte independentes

AGOV como infraestrutura de identidade digital

  • Identidade digital se tornou recentemente um dos grandes debates da web, acompanhada de várias interdependências e controvérsias
  • O sistema de login do governo suíço AGOV está em operação desde 2024 e chegou a 1,6 milhão de contas
  • Ele vem sendo ampliado como meio de login para vários serviços governamentais, incluindo o uso do seguro-desemprego e a declaração obrigatória de impostos

Cadastro travado na etapa de verificação por e-mail

  • O processo de cadastro do AGOV começa com o envio de um endereço de e-mail e a inserção de um código de verificação de 6 dígitos
  • A interface do código de verificação é composta por 6 caixas de entrada, uma para cada dígito
    • Ao digitar um número, o foco não passa para a próxima caixa
    • Os números continuam se acumulando na caixa atual, que muda para um estado de erro em vermelho
    • Mesmo avançando manualmente e preenchendo um dígito por vez, no fim aparece o erro field required
  • Foi feita uma tentativa de alterar diretamente os valores do DOM nas ferramentas de desenvolvedor, mas não havia valores de formulário visíveis, e nenhum erro era registrado no console web
  • O resultado foi o mesmo usando outros endereços de e-mail ou endereços falsos como test@example.com
    • example.com é um domínio reservado conforme a RFC 6761

Descartando navegador e sistema operacional

  • No início, suspeitou-se que a combinação Firefox e Linux não fosse compatível ou que o CAPTCHA tivesse falhado
    • Pouco antes de o formulário do código de verificação aparecer, havia uma conexão com eu-api.friendlycaptcha.eu
    • A documentação oficial de requisitos lista apenas Windows e macOS como sistemas operacionais compatíveis
    • Por outro lado, o guia de chaves de segurança diz que Linux e Firefox também são compatíveis
  • O mesmo erro ocorreu nas seguintes 6 combinações de ambiente
    • Firefox, Chromium e Chrome no Linux
    • Firefox, Safari e Chrome no macOS
  • Em um notebook de trabalho, depois de digitar, o foco passava automaticamente para a próxima caixa, e também aparecia corretamente o erro Code entered is incorrect, indicando código incorreto em vez de código vazio
  • No computador macOS de uma amiga, os três navegadores também aceitaram o código de verificação
  • Não era possível depender de um computador fornecido pelo empregador para interagir com serviços governamentais no longo prazo; além disso, mesmo que o cadastro fosse feito no computador de trabalho, ainda havia a possibilidade de não conseguir fazer login no notebook pessoal

Estrutura de suporte que também impedia reportar o problema

  • O suporte oficial era oferecido apenas por formulário web, e esse formulário também exigia primeiro inserir um código de verificação por e-mail
  • A FAQ afirma que o suporte das instituições participantes é oferecido durante o horário comercial somente pela criação de tickets online
  • Não havia e-mail nem telefone, além do endereço postal da Chancelaria Federal Suíça
  • O recurso de feedback para deixar elogios, críticas ou solicitações ao AGOV também exige login com AGOV ou uma conta equivalente
  • Usuários que falhavam na verificação por e-mail caíam numa dependência circular, sem poder reportar o problema por causa do mesmo fluxo de autenticação
  • AGOV Access e dispositivos compatíveis

    • O app AGOV Access para Android tinha nota 1,4, mas não foi encontrado o mesmo problema de código de verificação nas avaliações do Google Play
    • Nas avaliações, havia muitos pedidos de suporte ao GrapheneOS, sistema operacional derivado do Android com foco em segurança e privacidade
    • Segundo a FAQ oficial, o app não funciona porque o framework reforçado contra modificações não autorizadas é incompatível com o GrapheneOS
    • A Chancelaria Federal instruiu o fornecedor a garantir compatibilidade
    • Atualmente, os métodos de segundo fator para cadastro e login são um smartphone iOS·Android padrão permitido ou uma chave de segurança; em ambos os casos, antes é necessário passar pela verificação por e-mail

A causa encontrada no JavaScript

  • A página carregava poucos recursos, mas o principal arquivo JavaScript minimizado e não ofuscado tinha 2,4 MB e, expandido, passava de 100 mil linhas
  • Ao buscar pela string field required, foi encontrada a lógica que define o estado do código de verificação
    • Se a variável do código de verificação não existir, o estado é definido como REQUIRED
    • Se o comprimento for diferente do comprimento exigido para o código, ele é tratado como WRONG
    • Se o comprimento estiver correto, usa VALID ou um estado de erro conforme o resultado da verificação de e-mail no servidor
  • A função verificationCodeEntered intercepta as teclas e coleta o código de verificação em uma variável JavaScript
    • Enter executa o callback de verificação
    • Backspace apaga o valor atual
    • As setas e Tab movem entre as caixas de entrada
    • Teclas comuns são convertidas em número com Number(S.key) e armazenadas no código
    • Teclas como Control, Meta, Shift, v e r são retornadas sem processamento
  • Como, no envio do formulário, os valores do DOM não são lidos, alterar as caixas de entrada pelas ferramentas de desenvolvedor ou por extensões do navegador não se reflete no estado mantido separadamente pelo JavaScript

Por que os números eram bloqueados apenas no AZERTY

  • O layout francês padrão AZERTY exige a tecla Shift para digitar números
  • Como o código ignorava o Shift, no AZERTY não era possível armazenar os próprios números como código de verificação
  • O computador de trabalho usava layout QWERTY em inglês, e nenhuma das pessoas que ajudaram a confirmar o problema era francesa
    • Como resultado, entre os dispositivos testados, parecia que apenas os dois notebooks pessoais estavam com defeito
  • Ao trocar o layout de teclado no sistema operacional para outro layout, a digitação voltou ao normal nos notebooks pessoais
    • Inversamente, ao mudar um dispositivo que funcionava para o layout francês, o mesmo erro foi reproduzido
  • O teclado suíço padrão em francês pertence à família QWERTZ, comum na Europa Central
    • Ele permite digitar francês e alemão com eficiência e não exige Shift para números, então o mesmo erro não aparece
  • Segundo o Swiss Federal Statistical Office, há cerca de 171 mil residentes de nacionalidade francesa na Suíça, e esse número não inclui trabalhadores transfronteiriços que precisam interagir com o governo suíço

Limitações da investigação até a abertura do código-fonte

  • Segundo a FAQ do AGOV, o código-fonte do AGOV deve ser publicado em dezembro de 2026 para cumprir requisitos legais
  • O artigo 9º da EMBAG exige que órgãos federais publiquem o código-fonte de softwares que desenvolvem diretamente ou cuja criação encomendam para cumprir suas atribuições
    • Exceções se aplicam quando direitos de terceiros ou motivos de segurança impedem ou limitam a publicação
  • O escopo de publicação inclui a versão do AGOV que atingiu os objetivos do projeto, incluindo o AGOV e-ID Verifier, e versões posteriores devem ser publicadas por meio de notas de versão preparadas após cada lançamento
  • Embora alguns serviços já usem o AGOV como login obrigatório, o código-fonte ainda não foi publicado, dificultando que usuários avisem desenvolvedores diretamente sobre bugs ou compartilhem soluções alternativas

O escopo da verificação de identidade confirmado após o cadastro

  • Depois de alterar o layout do teclado, o endereço de e-mail foi verificado e o cadastro foi concluído com uma chave de segurança
  • Durante o cadastro, foram informados nome, telefone e data de nascimento, mas essas informações não foram verificadas
  • Na etapa básica de cadastro, apenas duas coisas foram confirmadas
    • O endereço de e-mail realmente existe
    • O método de segundo fator é uma chave de segurança de um modelo permitido ou o app AGOV Access em execução em um dispositivo Android·iOS permitido
  • O AGOV é robusto contra tomada de conta por phishing remoto, mas, na etapa básica de cadastro, não impede que alguém crie uma conta usando o nome e os dados de identidade de outra pessoa
  • Uma verificação adicional mostrou que, ao acessar serviços classificados como sensíveis, é necessária verificação de identidade
    • Logo após o cadastro, esses serviços ainda não haviam sido acessados
  • O bug foi reportado pelo formulário de suporte, mas não houve resposta

Lições do projeto

  • Projeto frágil de entrada e suporte

    • A abordagem de criar uma UI vistosa com 6 campos de entrada e lógica JavaScript complexa não é robusta
    • É melhor usar um único campo de entrada nativo do navegador e estilizar apenas a aparência visual com CSS
    • Quando a lógica do formulário é totalmente separada do DOM, os valores do DOM não podem ser lidos no envio, e usuários avançados ou extensões do navegador também não conseguem corrigir o valor inserido
    • Se houver apenas um canal de suporte, quando esse próprio canal quebra, desaparece o meio de contato; por isso é necessário um segundo canal, como e-mail ou telefone
    • Aplicar a mesma lógica de login ao produto principal e ao canal de suporte faz com que relatos de bugs de usuários que não conseguem fazer login sejam perdidos
    • É pelo mesmo motivo que vários serviços de internet mantêm páginas de status em domínios separados
    • Colocar solicitações de suporte atrás de verificação por e-mail pode reduzir spam, mas também reduz relatos de falhas de usuários reais
    • Se o código-fonte só for publicado depois que o uso já se tornou obrigatório em alguns sistemas, a solução de problemas por usuários e o compartilhamento de bugs e contornos ficam limitados
    • A publicação legal do código-fonte vai além de uma simples exigência de conformidade e também é útil para investigar falhas reais
  • A web aberta ajudou na investigação

    • Como a lógica JavaScript relevante não estava ofuscada, algumas buscas por strings bastaram para encontrar a causa
    • Não foram necessários descompiladores avançados nem LLMs caros
    • Se o código estivesse ofuscado ou compilado para WebAssembly, entender a causa teria levado muito mais tempo
    • Como o código não era distribuído apenas em sistemas operacionais fechados aprovados, mas disponibilizado na web aberta, foi possível baixá-lo e investigá-lo mesmo em forma minimizada
  • Condições que permitiram identificar a causa

    • Comparar vários notebooks com layouts de teclado diferentes permitiu reduzir cedo a possibilidade de problema específico de navegador ou de rede
    • Ainda assim, a coleta inicial de evidências levou tempo, pois foi necessário capturar sistematicamente as telas de cada combinação de navegador para confirmar que não era erro do usuário

1 comentários

 
GN⁺ 3 시간 전
Opiniões do Lobste.rs
  • Uma das práticas que mais odeio na internet é quando sites interceptam a digitação e processam tudo do próprio jeito, em vez de simplesmente deixar o navegador lidar com isso
    Sites que proíbem copiar e colar em campos de senha são muito mais comuns, mas isso é da mesma categoria e talvez pareça ainda pior

    • Não entendo por que reimplementar em JavaScript algo que o navegador já suporta nativamente só com HTML e CSS
      Na maioria das vezes isso só aumenta a complexidade por motivos forçados
  • A abordagem de usar um campo por caractere virou uma praga
    Uma empresa adotou isso porque parecia estiloso, e agora todo mundo está fazendo sua própria versão

    • Não me surpreenderia se esse tipo de UI realmente aumentasse a taxa de sucesso na digitação de códigos de uso único
      Mesmo sem ler com atenção, o usuário percebe na hora que não é um campo de senha
  • Como caso relacionado, há Medium once had a bug like this which prevented entering the character 'Ś'

  • Pensei em outras formas de implementar isso, mas no fim não consigo encontrar nada melhor do que um <input type="text" /> comum
    Até fico em dúvida sobre usar decoração em CSS que faça isso parecer muito diferente de um campo normal. Dá para duplicar visualmente o valor digitado em caixas grandes, mas aí surgem todo tipo de questão, como o que fazer quando o usuário digita mais do que o tamanho permitido, e os benefícios não parecem grandes o bastante para justificar o esforço. No máximo eu mudaria a fonte e o tamanho do campo

    • O erro mais comum em OTP é digitar um número errado, como 1224 em vez de 1234
      A forma mais rápida de corrigir é clicar no segundo 2 e apertar 3, mas isso não dá para fazer só com HTML+CSS
    • Admito que é uma ideia meio ridícula, mas fico pensando se daria para aplicar um espaçamento de letras incomum em um único <input type="text" pattern="[0-9]+" minlength=6 maxlength=6> para que ele pareça visualmente seis caixinhas
      Uma forma mais simples seria o usuário digitar em um único campo de texto e o JavaScript desenhar seis caixas em outro ponto do DOM, atualizando tudo pelo evento input disparado a cada tecla
      É difícil ter certeza de qual opção seria melhor em termos de acessibilidade. Do ponto de vista de leitor de tela, a segunda opção — um <input> normal junto com um <canvas alt=""> decorativo — talvez até seja melhor, e a navegação por teclado não ficaria estranha. Mas para usuários que dependem de recursos assistivos além de leitores de tela, esconder visualmente o <input> pode virar um desastre de acessibilidade, então eu seria muito cauteloso
  • Como muitos serviços fazem, também daria para fornecer o código e um link de ativação no e-mail

    Inserir código: XX XX XX
    
    Ou clicar no link: <redirecionamento + link de ativação com código>  
    

    Isso também pode resolver o mesmo problema

    • Acho que acostumar os usuários a clicar em links de e-mail é enfraquecer a defesa contra phishing
    • Se você abriu o site no notebook e recebeu o e-mail no celular, copiar e colar não é tão prático assim
      Pode parecer uma situação excepcional, mas acessibilidade é justamente lidar bem com esse tipo de exceção
  • O Tridactyl também tem exatamente o mesmo problema https://github.com/tridactyl/tridactyl/issues/3257
    Foi reportado pela primeira vez em 2019, então talvez esse caso ao menos faça o pessoal criar vergonha e corrigir

  • Isso mostra mais uma vez que dá muito mais trabalho fazer um site quebrado ou lento do que simplesmente fazer um site que funcione