IDで視覚的に紛らわしい文字を避ける
(gajus.com)- バグ報告、割引コード入力、配送追跡のように人がIDを読んで伝える場面では、
O/0、I/l/1/7のような 視覚的な曖昧さ が入力ミスに直結しやすい - 混同はフォントや手書きによって大きくなり、
5/S、2/Z、8/B、6/G、9/q/gのような 区別が曖昧になる組み合わせ が繰り返し現れる - カスタマーサポート、エラーID、製品IDのように人が直接扱うIDでは、むやみに大きな文字集合よりも 読みやすい文字集合 を選ぶほうが実運用品質にとって重要である
- 大文字小文字を区別すれば5文字IDで
418,195,493通りの組み合わせを作れるが、区別しなければ5,153,632通りまで減るため、長さと安全性のトレードオフ が必要になる - 大文字小文字を区別するIDは短く作りやすいが、一部のサードパーティシステムやプロトコルが 大文字小文字を区別しない 動作をする可能性があり、統合段階で問題が起きることがある
人が読むIDで起きる文字の混同
- バグレポート、割引コード入力、配送追跡のようなシステムとのやり取りでIDを書いたり伝えたりする際、避けられる文字の混同がユーザー体験を損なうことがある
- 代表的な 視覚的に紛らわしい文字 の組み合わせは次のとおり
O/0: スラッシュやドットのない 0 では、文字のOと数字の0が似て見えることがあるI/l/1/7: 大文字のI、小文字のl、数字の1、数字の7は、多くの印刷物や手書きで区別しにくい5/S、2/Z、8/B、6/G、9/q/g: 特定のフォント、装飾的なフォント、手書きで混同されることがある
- 例示文字列
9qg6G8B2Z5SIl170Oは、Arial、Helvetica、Courier、Times、Verdana、Georgia、Tahoma、Impact、Comic Sans など複数のシステムフォントで比較対象として使われている Iとlのように、いくつかの組み合わせは多くのフォントで継続的に曖昧に見えることがあり、9qgのように実際に手で書くとさらに紛らわしい組み合わせもある-
特に問題が大きくなるIDの種類
- カスタマーサポートでやり取りされる割引コード
- 配送や物流で使う追跡コード
- 問題再現や問い合わせ対応に必要なエラーID
- 製品識別に使う製品ID
大文字小文字の区別と文字集合のトレードオフ
- IDが
abcとABCを同じ値とみなすかどうかは、生成ルールを決める際にあわせて決定する必要がある - 大文字小文字を区別しつつ視覚的に紛らわしい文字を除外すると、選択可能な文字は 53文字 である
- 大文字小文字を区別しない場合、選択可能な文字は 22文字 に減る
- IDの長さごとの組み合わせ数は次のとおり
- 5文字、大文字小文字を区別:
53^5 = 418,195,493 - 5文字、大文字小文字を区別しない:
22^5 = 5,153,632 - 8文字、大文字小文字を区別:
53^8 = 62,259,690,411,361 - 8文字、大文字小文字を区別しない:
22^8 = 54,875,873,536
- 5文字、大文字小文字を区別:
- 結局のところ選択肢は、短いが混同されやすいID と 長いが読みやすいID のあいだでバランスを取ることになる
- 大文字小文字の両方を使うと、いずれ大文字小文字を区別しないサードパーティシステムやプロトコルで予期しない挙動に遭遇する可能性がある
- ある商用システムでは、ユーザーが
iDとidを別々のIDとして選べるようにしていたが、存在しないIDを検索するときに大文字小文字を区別しないマッチングを行い、誤ったデータを返していた - そのバグには「利便性」のための挙動だという回答が返ってきた
- ある商用システムでは、ユーザーが
単一の文字だけでなく組み合わせも避ける必要があるかもしれない
- 文字の組み合わせ自体が別の文字のように見える場合もある
rnはmのように見えることがあるvvはwのように見えることがある
- この理由だけで大量の文字を除外すると選択可能な文字集合が小さくなりすぎるため、生成段階で特定の組み合わせだけを避ける方法のほうが現実的かもしれない
- 口頭で伝える状況では、音声的な類似性 もあわせて考慮できる
- たとえば
bとpは発音すると似て聞こえることがある
- たとえば
参考になる既存の方式
- CrockfordのBase32 は、曖昧な文字を同じ値としてデコードし、偶発的に卑猥な表現になる問題も考慮している
- Open Location Code は
23456789CFGHJMPQRVWXの文字集合を使用している- この文字集合は、視覚的に紛らわしい文字を避け、一般言語の単語の綴りも避けるよう選ばれている
- ただし
6とG、9とQはいずれも含まれている
3件のコメント
これもよさそうです: https://stackoverflow.com/a/58098360/8556340
発音まで考慮しているなんて、本当に驚異的ですね。
Hacker News のコメント
職場で数百万台の機器にシリアル番号を付けて出荷したのだが、紛らわしい文字や数字をまったく除外していなかったため、顧客が正しく読み取るのに大いに苦労したことがある
顧客が伝えた値をもとに、考えられるすべてのタイプミスの組み合わせを生成し、工場データベースと一致するものだけを表示する正規表現スクリプトを作る必要があり、その後、日付など他の情報と照合して実際のシリアル番号を推定していた
さらに皮肉なのは、いくつかの桁は絶対に変わらず、ある桁は工場識別用として 0、1、2 だけを使えばよかったので、そもそも文字集合全体は必要なかったという点だ。まるで8000兆台作るつもりだと信じていたかのようだった
たとえば製品に 1、2、3 のような連番を付けると、少ないサンプルだけでも総販売数をかなり簡単に推定できる。有効なシリアル番号を推測しにくくすることで、返金の悪用のようなことを防ぐ助けになる場合もある
もちろん、そうした懸念があっても読みづらい文字を避ける対策は併用できるし、むしろどんな番号体系を使うかを誰かが考えているという意味なら、この種の問題をもっと認識しているべきだった。実際には、誰かが 30 秒ほど考えて「桁数をこれだけ使えば絶対に尽きないな、終わり」とした可能性が高そうだ
デバイスのシリアル番号には子音と数字だけが使われているように見える
エンコーディングはユーザーによって変えるべきだ。Base32、特に Crockford と RFC 4648 は短く表現でき、曖昧でないアルファベットとその理由づけもよく整っている
ただし、ユーザーが声に出して伝える必要があるなら、s/key RFC 1751 のような「TIDE ITCH SLOW REIN RULE MOT」といった単語リスト表現の方がよい場合がある
自分で単語リストを作ってはいけない。慣用句、同音異義語、方言など、隠れた落とし穴が際限なくある。「wet clam butterfly」のような大惨事を意図せず作らないようにすべきだ
この例が出てくる RFC 1751 [0] は口頭伝達用のエンコーディングを想定しておらず、ユーザーがより簡単に「読んで、記憶して、入力」できるようにすることが目的だ
専門家同士が口頭で伝えるなら、大文字 26 文字だけを使い、NATO フォネティックコードに頼る選択は合理的だ。しかし、訓練を受けていないユーザーから雑音の多い口頭環境でコードを聞き取る問題は、依然として未解決の問題だ
[0] https://datatracker.ietf.org/doc/html/rfc1751
「鍵付きメッセージダイジェストアルゴリズム MD5 を使用すべきであり、十分に強い」というくだりからして笑える
「ほとんどの人にとって読んで、記憶して、入力するのが難しい」まではよかったのに、「英単語は人々が記憶して入力するのがずっと簡単だ」へ進んでしまう。ほとんどの人は英語を知らないという問題があるが、単語リストを変えればいいのではと思ったら、「相互運用性のため、言語別の辞書を別途用意するのは望ましくない」としている
結局、世界中の人が英字 26 文字は学んでいるのだから単語をいくつか追加しても大丈夫、というような話なのだが、
char Wp[2048][4] = […]の中には初心者向けの一般的な単語ではなく、「WAD, BESS, MERT…」のようなものが入っている。さらには「ORR? AGEE EGAN HAAS!!」「GAUL FLAM! DRAB!」まで出てくるこの話を見て昔のことを思い出した。病気の日に体調の悪さを忘れるため、任意基数の算術をするおもちゃのモジュールを作り、簡単だったので CPAN に上げた
そのモジュールが https://metacpan.org/pod/Math::Fleximal だ
自分が作った些細なものの中でも、これは絶対にサポート依頼など来ないだろうと思っていたのに、実際に来た。理由は、16 進数を英数字コードに変換する例を入れておいたところ、誰かが長い数字を読みやすいコードに変換するのにそのまま使おうという見事なアイデアを思いついたからだ
モジュール自体はうまく動いたが、これがどこかの本番環境に入ったという事実はかなり荒唐無稽だった
記事では手書きでも区別しにくい文字を避けようと強調しているのに、例の表には数字の 7 が入っている。誰かの 7 と 1 を区別しにくかったことは数え切れないほどある
7 に横棒を入れれば助けになるが、多くの人はそう書かないので、7 なのかセリフ付きの 1 なのか確信しづらいことがある
この 2 つはほとんど区別できないことがある。NATO/航空標準アルファベット(Alpha, Bravo, Charlie, Delta...)を使うこともできるが、顧客層が非常に厳密に限定されていない限り、大きな助けにはならない。こうした組み合わせも避ける方がよい
ID 文字列が少し長くなっても、文字を読み、話し、聞き取る能力を最大化する方がはるかに良く、時間といら立ちをずっと多く減らしてくれる
どんな手書きスタイルだと 1 が 7 に似るのか気になる。7 の上の横棒だけでも十分に区別できそうだ
大文字と小文字の両方を使うと、いつか大文字小文字を区別しないサードパーティのシステムやプロトコルにやられる可能性が高い
実際、ユーザーが大文字小文字を区別する ID を選べるようにして
iDとidを別々の値として許可している一方で、存在しない ID を照会すると大文字小文字を無視してマッチングし、見当違いのデータを返す商用システムを見たことがあるこのバグを報告したところ、「利便性のための機能です」と返答された
Nintendo SwitchでDLCのシリアル番号を入力するとき、紛らわしい文字のキーが画面キーボードで無効化されていて、かなり良いユーザー体験だと感じた
これは、そもそもシリアル番号が紛らわしい文字を含まないように生成されているという意味だ。このUXがOSに組み込まれているものなのか、それともプレイしていたゲームであるMario + Rabbids Sparks of Hopeだけにあったものなのかはよく分からない
オープンソースのパスワードマネージャーKeepassXCは、パスワードを読みやすくするために色を使っている。大文字、小文字、数字、記号といった文字種ごとに別の色を使う方式だ
とても単純なアイデアだが、特にランダムなパスワードでは、すでに可読性の高いフォントを使っていても大いに役立つ
パスワード中心のソフトウェアが、色分けもなしに紛らわしいフォントで文字をレンダリングするのを許しているのは理解しがたい
TVリモコンのようなインターフェースで長いパスワードを入力していて、
l1|Iを取り違えていたと気づく瞬間があまりに腹立たしいので、自分で除外している普段よく直面する問題を扱った記事なので、読んでいてよかった
紙に2段階認証のバックアップコードを書くたびに、
o/0、v/u、5/Sのような文字を通ると不安になる。なので、こうした文字はわざと違って見えるように少し装飾して書くようになった「音声上の類似性」の部分を見て、Wi-Fiパスワードを選ぶときのことを思い出した。1文で共有しても曖昧にならず、小学3年生でも綴れて、子音が複数入っている一般的な単語が欲しくて、結局「vacation」を選んだ
5/S、0/O、8/Bのような問題が解決する。実際に問題になる組み合わせは、それぞれの手書き文字によって異なる本当に確信が持てないときは、NATO/航空アルファベット [1]も添える。例えば
Uがあれば、Uから斜めに書き始めてUniformと書く必要なのは少しの規律だけだ。10年以上こうしてきて、2段階認証コードをなくしたことは一度もない
[1] NATOと航空コードの実際の違いについての揚げ足取りは、安全に
/dev/nullへ送ってよい頭がボトルネックになる
こういう会話が好きだ。最先端でも胸躍るテーマでもないかもしれないが、人間と機械のどちらにとっても生活を楽にするうえで、かなりの意味と力がある
こうしたものは、うまくできているとむしろ誰にも気づかれないベストプラクティスの領域でもある。細部への誠意や真摯さが「当然そうあるべきもの」として片づけられ、特に称賛されないのは残念な現実だ
記事のミスを指摘すると、
9qg6G8B2Z5SIl170O (ariel)でのフォント名はArielではなくArialだ。人魚姫はここにはいないLinuxではほとんどの行が同じに見える
https://github.com/gajus/gajus-com/blob/main/src/blogPosts/2...
タイポは直しておいた