Evitar caracteres visualmente ambíguos em IDs
(gajus.com)- Em processos nos quais pessoas precisam ler e repassar um ID, como relatórios de bug, digitação de cupons de desconto e rastreamento de entregas, a ambiguidade visual de pares como
O/0eI/l/1/7facilmente leva a erros de digitação - A confusão aumenta conforme a fonte e a caligrafia, e combinações com distinção pouco nítida como
5/S,2/Z,8/B,6/Ge9/q/gaparecem repetidamente - Em IDs que pessoas manipulam diretamente, como suporte ao cliente, IDs de erro e IDs de produto, escolher um conjunto de caracteres fácil de ler é mais importante para a qualidade de uso real do que simplesmente usar um conjunto de caracteres maior
- Fazendo distinção entre maiúsculas e minúsculas, é possível criar
418,195,493combinações com um ID de 5 caracteres, mas sem essa distinção o total cai para5,153,632, exigindo um equilíbrio entre comprimento e segurança - IDs com distinção entre maiúsculas e minúsculas ajudam a mantê-los curtos, mas alguns sistemas ou protocolos de terceiros podem funcionar de forma case-insensitive, o que pode causar problemas na etapa de integração
Confusão de caracteres em IDs lidos por pessoas
- Ao escrever ou transmitir IDs em interações de sistema como relatório de bugs, entrada de cupons de desconto e rastreamento de entregas, confusões de caracteres evitáveis podem arruinar a experiência do usuário
- As combinações mais representativas de caracteres visualmente ambíguos são as seguintes
O/0: em zeros sem barra ou ponto, a letraOe o número0podem parecer semelhantesI/l/1/7: a letra maiúsculaI, a letra minúsculal, o número1e o número7podem ser difíceis de distinguir em vários impressos e em caligrafia5/S,2/Z,8/B,6/G,9/q/g: podem causar confusão em certas fontes, fontes estilizadas e escrita à mão
- A string de exemplo
9qg6G8B2Z5SIl170Oé usada como comparação em várias fontes de sistema, como Arial, Helvetica, Courier, Times, Verdana, Georgia, Tahoma, Impact e Comic Sans - Algumas combinações, como
Iel, podem continuar ambíguas em várias fontes, e outras, como9qg, podem ficar ainda mais confusas quando escritas à mão -
-
Tipos de ID em que o problema se agrava especialmente
- cupons de desconto trocados no suporte ao cliente
- códigos de rastreamento usados em entregas ou logística
- IDs de erro necessários para reproduzir problemas e tratar atendimentos
- IDs de produto usados para identificação de produtos
-
Equilíbrio entre diferenciação de maiúsculas/minúsculas e conjunto de caracteres
- Ao definir as regras de geração, também é preciso decidir se o sistema tratará
abceABCcomo o mesmo valor ou não - Excluindo caracteres visualmente ambíguos e diferenciando maiúsculas de minúsculas, restam 53 caracteres disponíveis
- Sem diferenciar maiúsculas de minúsculas, o número de caracteres disponíveis cai para 22
- O número de combinações possíveis conforme o comprimento do ID é o seguinte
- 5 caracteres, com distinção entre maiúsculas e minúsculas:
53^5 = 418,195,493 - 5 caracteres, sem distinção entre maiúsculas e minúsculas:
22^5 = 5,153,632 - 8 caracteres, com distinção entre maiúsculas e minúsculas:
53^8 = 62,259,690,411,361 - 8 caracteres, sem distinção entre maiúsculas e minúsculas:
22^8 = 54,875,873,536
- 5 caracteres, com distinção entre maiúsculas e minúsculas:
- No fim, a escolha é um equilíbrio entre IDs curtos, mas mais propensos a confusão e IDs mais longos, porém mais fáceis de ler
- Se você usar maiúsculas e minúsculas, em algum momento pode encontrar comportamentos inesperados em sistemas ou protocolos de terceiros que tratam IDs sem diferenciar maiúsculas de minúsculas
- Um sistema comercial permitia que usuários escolhessem
iDeidcomo IDs diferentes, mas ao consultar um ID inexistente fazia correspondência sem diferenciar maiúsculas de minúsculas e retornava dados incorretos - A resposta para esse bug foi que se tratava de um comportamento por “conveniência”
- Um sistema comercial permitia que usuários escolhessem
Talvez seja preciso evitar combinações, não apenas caracteres isolados
- Em alguns casos, a própria combinação de caracteres pode parecer outro caractere
rnpode parecermvvpode parecerw
- Excluir muitos caracteres apenas por esse motivo pode reduzir demais o conjunto disponível, então pode ser mais realista evitar apenas combinações específicas na etapa de geração
- Em situações de transmissão por voz, também vale considerar a semelhança sonora
- Por exemplo,
beppodem soar parecidos quando pronunciados
- Por exemplo,
Abordagens existentes que valem como referência
- Base32 de Crockford decodifica caracteres ambíguos como o mesmo valor e também considera o problema de formar expressões obscenas acidentalmente
- Open Location Code usa o conjunto de caracteres
23456789CFGHJMPQRVWX- Esse conjunto foi escolhido para evitar caracteres visualmente ambíguos e também evitar a formação de palavras de idiomas comuns
- Ainda assim, inclui tanto
6eGquanto9eQ
3 comentários
Isso também parece bom: https://stackoverflow.com/a/58098360/8556340
É realmente impressionante que tenham levado em conta até a pronúncia.
Comentários no Hacker News
No trabalho, já enviamos milhões de dispositivos com números de série, mas sem excluir nenhum caractere ou número confuso, e os clientes tiveram muita dificuldade para lê-los corretamente
Tive de criar um script de regex que gerava todas as combinações possíveis de erros de digitação a partir do valor informado pelo cliente e mostrava apenas as que batiam com o banco de dados da fábrica; depois, cruzávamos outras informações, como a data, para inferir o número de série real
O mais irônico é que alguns dígitos nunca mudavam e, em certa posição, bastava usar 0, 1 e 2 para identificar a fábrica, então nem era necessário usar o conjunto inteiro de caracteres. Era como se acreditassem que iríamos fabricar 8 quatrilhões de unidades
Por exemplo, se você numera produtos sequencialmente como 1, 2, 3, é bem fácil estimar o total de vendas com uma amostra pequena. Tornar difícil adivinhar números de série válidos também pode ajudar a evitar coisas como abuso de reembolsos
Claro que, mesmo com essas preocupações, ainda dá para evitar caracteres difíceis de ler; aliás, se isso significa que alguém está pensando em que esquema de numeração usar, a pessoa deveria ter percebido melhor esse problema. Na prática, parece bem provável que alguém tenha pensado por uns 30 segundos e dito: “se usarmos tantos dígitos, nunca vai acabar, pronto”
Parece que os números de série dos dispositivos usam apenas consoantes e números
A codificação deve variar conforme o usuário. Base32, especialmente Crockford e RFC 4648, é bom para uma representação curta e tem alfabetos não ambíguos bem definidos, com boas justificativas
Mas, se o usuário precisar falar em voz alta, uma representação por lista de palavras como no s/key RFC 1751, do tipo “TIDE ITCH SLOW REIN RULE MOT”, pode ser melhor
Não crie sua própria lista de palavras. Há infinitas armadilhas escondidas, como expressões idiomáticas, homófonos, dialetos etc. Não crie sem querer um desastre do tipo “wet clam butterfly”
A RFC 1751 [0], de onde vem esse exemplo, não pressupunha uma codificação para transmissão oral; o objetivo era tornar mais fácil para o usuário “ler, lembrar e inserir”
Se a transmissão oral for entre especialistas, faz sentido usar apenas as 26 letras maiúsculas e se apoiar no alfabeto fonético da OTAN. Mas obter um código de usuários sem treinamento em um ambiente oral com muito ruído continua sendo um problema em aberto
[0] https://datatracker.ietf.org/doc/html/rfc1751
Já dá para rir no trecho “deve-se usar o algoritmo de digest de mensagem com chave MD5, que é suficientemente forte”
Até “é difícil para a maioria das pessoas ler, lembrar e inserir” estava bom, mas aí passa para “palavras em inglês são muito mais fáceis para as pessoas lembrarem e digitarem”. Há o problema de que a maioria das pessoas não sabe inglês, mas pensei que talvez bastasse trocar a lista de palavras; então vem “por interoperabilidade, não é desejável ter dicionários separados por idioma”
No fim, a lógica é que o mundo inteiro aprendeu as 26 letras do alfabeto inglês, então acrescentar algumas palavras deve ser aceitável, mas dentro de
char Wp[2048][4] = […]não há palavras comuns adequadas para iniciantes, e sim coisas como “WAD, BESS, MERT…”. Aparecem até “ORR? AGEE EGAN HAAS!!” e “GAUL FLAM! DRAB!”Essa discussão me lembrou de algo antigo. Em um dia em que eu estava doente, para esquecer a dor no corpo, criei um módulo de brinquedo para fazer aritmética em bases arbitrárias; como era simples, coloquei no CPAN
O módulo é https://metacpan.org/pod/Math::Fleximal
Entre as coisas triviais que criei, achei que essa jamais receberia pedido de suporte, mas recebeu. O motivo foi que eu tinha incluído um exemplo de conversão de hexadecimal para um código alfanumérico, e alguém teve a brilhante ideia de usá-lo exatamente assim para transformar números longos em códigos mais legíveis
O módulo funcionava bem, mas foi bem absurdo descobrir que isso tinha entrado em produção em algum lugar
O texto enfatiza evitar caracteres difíceis de distinguir até na escrita à mão, mas a tabela de exemplos inclui o número 7. Já passei por incontáveis situações em que era difícil distinguir o 7 e o 1 de alguém
Colocar um traço horizontal no 7 ajuda, mas muita gente não escreve assim, então às vezes é difícil ter certeza se é um 7 ou um 1 com serifa
Às vezes os dois são quase indistinguíveis. Dá para usar o alfabeto padrão da OTAN/aviação (Alpha, Bravo, Charlie, Delta...), mas isso não ajuda muito se o público de clientes não for muito estritamente delimitado. É melhor evitar esse tipo de combinação também
Mesmo que a string de ID fique um pouco mais longa, é muito melhor maximizar a capacidade de ler, falar e ouvir os caracteres, e isso economiza muito mais tempo e irritação
Fico curioso para saber em que estilo de escrita manual o 1 fica parecido com 7. Só o traço superior do 7 já me parece suficiente para diferenciá-los
Se você usa maiúsculas e minúsculas, em algum momento vai acabar sendo atingido por algum sistema ou protocolo de terceiros case-insensitive
Já vi um sistema comercial que permitia ao usuário escolher IDs com distinção entre maiúsculas e minúsculas, aceitando
iDeidcomo valores diferentes, mas, ao consultar um ID inexistente, fazia correspondência ignorando maiúsculas/minúsculas e retornava os dados erradosQuando relatei esse bug, responderam que era um “recurso de conveniência”
Ao inserir um número de série de DLC no Nintendo Switch, as teclas de caracteres ambíguos ficavam desativadas no teclado na tela, e achei isso uma experiência de usuário bem boa
Isso significa que os números de série já são gerados, desde o início, sem caracteres ambíguos. Não tenho certeza se essa UX é integrada ao sistema operacional ou se existia apenas no jogo que eu estava jogando, Mario + Rabbids Sparks of Hope
O gerenciador de senhas open source KeepassXC usa cores para tornar as senhas mais legíveis. A ideia é usar cores diferentes para cada tipo de caractere, como maiúsculas, minúsculas, números e símbolos
É uma ideia muito simples, mas ajuda bastante, especialmente em senhas aleatórias, mesmo quando já se usa uma fonte de alta legibilidade
É difícil entender como softwares centrados em senhas permitem renderizar caracteres com uma fonte ambígua sem nenhuma diferenciação por cor
É tão irritante estar digitando uma senha longa por uma interface como a de um controle remoto de TV e perceber que confundiu
l1|I, que eu mesmo acabo excluindo esses caracteresGostei de ler o texto porque trata de um problema que encontro com frequência no dia a dia
Sempre que escrevo em papel códigos de backup de autenticação em duas etapas, fico inseguro ao passar por caracteres como
o/0,v/u,5/S. Por isso passei a escrever esses caracteres de propósito de um jeito um pouco ornamentado para que pareçam diferentesA parte sobre “semelhança fonética” me lembrou de quando escolhi uma senha de Wi-Fi. Eu queria uma palavra comum, com várias consoantes, que não fosse ambígua mesmo compartilhada em uma frase, e que até uma criança do 3º ano do ensino fundamental conseguisse soletrar; no fim escolhi “vacation”
5/S,0/O,8/B. Na prática, os pares problemáticos variam conforme a caligrafia de cada umSe eu realmente não tiver certeza, também acrescento o alfabeto NATO/aeronáutico [1]. Por exemplo, se houver um
U, escrevoUniformna diagonal a partir doUSó é preciso um pouco de disciplina. Faço isso há mais de 10 anos e nunca perdi um código de autenticação em duas etapas
[1] Picuinhas sobre a diferença real entre os códigos NATO e aeronáuticos podem ser enviadas com segurança para
/dev/nullA cabeça vira o gargalo
Gosto desse tipo de conversa. Pode não ser um tema de ponta nem empolgante, mas tem bastante significado e força para tornar a vida tanto das pessoas quanto das máquinas mais fácil
Essas coisas também pertencem ao campo das boas práticas, em que, quando são bem feitas, ninguém sequer percebe. É uma realidade triste que o cuidado e a sinceridade com os detalhes acabem agrupados como “é assim que deveria ser” e não recebam elogios especiais
Para apontar um erro do texto: em
9qg6G8B2Z5SIl170O (ariel), o nome da fonte não é Ariel, e sim Arial. A Pequena Sereia não aparece aquiNo Linux, a maioria das linhas parece idêntica
https://github.com/gajus/gajus-com/blob/main/src/blogPosts/2...
Já corrigi o typo