1 ポイント 投稿者 GN⁺ 3 시간 전 | 1件のコメント | WhatsAppで共有
  • スイス政府のログインシステム AGOV の6桁メール認証入力欄が、フランス式AZERTYキーボードでの数字入力を処理できず、アカウント登録そのものができなかった
  • 入力欄のJavaScriptはキー入力を横取りして認証コードを別の変数に保存していたが、Shiftキーを無視しており、数字入力にShiftが必要なAZERTY配列を事実上ブロックしていた
  • Firefox・Chromium・Chrome・Safari と Linux・macOS の複数の組み合わせで同じ不具合が発生したが、QWERTYキーボードでは正常に動作したため、ブラウザやOSではなく キーボード配列の問題 に絞り込まれた
  • 公式サポートも同じメール認証を通過しないと利用できず、メールや電話のような代替手段もなかったため、ログインできない利用者は ログインのバグを報告することすらできなかった
  • ネイティブ入力フィールドの代わりに複雑なキー処理ロジックを使い、製品とサポートチャネルを同じ認証手順に結び付けると、アクセシビリティと障害対応が同時に崩れるため、単純なDOMベースの入力 と独立したサポートチャネルが必要になる

デジタル身元インフラとなったAGOV

メール認証の段階で止まった登録

  • AGOV登録手順 は、メールアドレスを送信したあと 6桁の認証コード を入力するところから始まる
  • 認証コードUIは、数字ごとに1つずつ、合計6つの入力ボックスで構成されている
    • 数字を入力してもフォーカスが次のボックスへ移動しない
    • 現在のボックスに数字がたまり続け、赤いエラー状態に変わる
    • 1桁ずつ手動で移動して入力しても、最後には field required エラーが発生する
  • 開発者ツールでDOMの値を直接変更しようとしたが、目立つフォーム値は見当たらず、Webコンソールにもエラーは記録されなかった
  • 別のメールアドレスや test@example.com のようなダミーアドレスを使っても結果は同じだった
    • example.comRFC 6761 に従って予約されたドメインである

ブラウザとOSの可能性を排除

  • 当初は Firefox と Linux の組み合わせが未対応か、CAPTCHA が失敗しているのではないかと疑った
    • 認証コードフォームが表示される直前に eu-api.friendlycaptcha.eu へ接続する
    • 公式要件文書 には対応OSとして Windows と macOS のみが記載されている
    • 一方で セキュリティキー案内 には Linux と Firefox も対応と書かれている
  • 次の 6つの環境の組み合わせ で、すべて同じ不具合が発生した
    • Linux の Firefox、Chromium、Chrome
    • macOS の Firefox、Safari、Chrome
  • 仕事用ノートPCでは、入力後に自動で次のボックスへ移動し、空コードではなく誤ったコードであることを示す Code entered is incorrect エラーも正常に表示された
  • 友人の macOS マシンでも、3つのブラウザすべてが認証コードを受け付けた
  • 政府サービスと長期的にやり取りするために、雇用主支給のPCに依存することはできず、仕事用PCで登録できても個人ノートPCでログインできない可能性が残っていた

問題報告まで塞いだサポート構造

  • 公式サポート はWebフォームのみで提供されており、このフォームでも先にメール認証コードを入力する必要があった
  • FAQ には、参加機関のサポートは営業時間中の オンラインチケット作成のみ で提供されると明記されている
  • Swiss Federal Chancellery の郵送先住所以外に、メールアドレスや電話番号はなかった
  • AGOVに賞賛・批判・要望を送る フィードバック機能 も、AGOV か同等のアカウントでのログインを要求していた
  • メール認証に失敗した利用者は、同じ認証手順のせいで問題を報告することもできない 循環依存 に陥る
  • AGOV Access と対応デバイス

    • AGOV Access Androidアプリ の評価は1.4だったが、Google Play レビューでは同じ認証コード問題は見つからなかった
    • レビューには、セキュリティ・プライバシー重視のAndroid派生OS GrapheneOS への対応要望が多数あった
    • 公式FAQ によれば、不正改変防止用の強化フレームワークが GrapheneOS と互換性がないため、アプリは動作しない
    • Federal Chancellery はベンダーに互換性確保を指示した
    • 現在、登録とログインの二要素認証手段として許可されているのは、認可された標準 iOS・Android スマートフォンまたはセキュリティキーであり、いずれにしても先にメール認証を通過しなければならない

JavaScriptで見つけた原因

  • ページが読み込むリソースは少なかったが、難読化されていない最小化済みの主JavaScriptファイルは 2.4MB あり、整形すると10万行を超えていた
  • field required 文字列を検索して、認証コード状態を設定するロジックを見つけた
    • 認証コード変数がなければ状態を REQUIRED に設定する
    • 長さが要求されたコード長と一致しなければ WRONG として扱う
    • 長さが合っていれば、サーバーのメール認証結果に応じて VALID またはエラー状態を使う
  • verificationCodeEntered 関数はキー入力を横取りし、認証コードを JavaScript変数 に収集していた
    • Enter は認証コールバックを実行する
    • Backspace は現在の値を消去する
    • 矢印キーと Tab は入力ボックス間を移動する
    • 通常キーは Number(S.key) で数値化してからコードに保存する
    • ControlMetaShiftvr などのキーは処理せずにそのまま戻る
  • フォーム送信時にDOMの値を読まないため、開発者ツールやブラウザ拡張で入力ボックスを変更しても、JavaScriptが別管理している状態には反映されない

AZERTYでだけ数字がブロックされた理由

  • フランス標準の AZERTY 配列では、数字を入力する際に Shiftキーが必要 である
  • コードが Shift 入力を無視していたため、AZERTY では数字そのものを認証コードとして保存できなかった
  • 仕事用PCは英語の QWERTY 配列であり、問題の確認を手伝ってくれた人の中にフランス人はいなかった
    • その結果、テストした端末のうち個人ノートPC2台だけが壊れているように見えた
  • OSでキーボード配列を別の配列に切り替えると、個人ノートPCでも入力が正常化した
    • 逆に、正常動作していた端末をフランス式配列に切り替えると、同じ不具合が再現した
  • スイスの標準フランス語キーボードは、中欧で一般的な QWERTZ 系である
    • フランス語とドイツ語を効率よく入力でき、数字入力にShiftが不要なため、同じ不具合は起きない
  • Swiss Federal Statistical Office によれば、スイスにはフランス国籍の居住者が約 17万1,000人 おり、スイス政府とやり取りする必要がある越境通勤者はこの数に含まれていない

ソース公開前までの調査上の制約

  • AGOV FAQ によれば、AGOV のソースコードは法的要件を満たすため 2026年12月 に公開予定である
  • EMBAG第9条 は、連邦機関が業務遂行のために自ら開発した、または開発を委託したソフトウェアのソースコード公開を求めている
    • 第三者の権利やセキュリティ上の理由で公開が妨げられる、または制限される場合には例外が適用される
  • 公開対象は AGOV e-ID Verifier を含む、プロジェクト目標を達成した AGOV バージョンであり、その後のバージョンはリリース後に作成されるリリースノートとともに公開される予定である
  • 一部サービスはすでにAGOVを必須ログインとして使っているが、ソースはまだ公開されていないため、利用者が開発者に直接バグを伝えたり、回避方法を共有したりするのは難しかった

登録後に確認した身元検証の範囲

  • キーボード配列を変更したあと、メールアドレスを認証し、セキュリティキー で登録を完了した
  • 登録過程では氏名、電話番号、生年月日を入力したが、これらの情報は検証されなかった
  • 基本登録段階で確認されたのは、次の2点だけだった
    • メールアドレスが実在すること
    • 二要素認証手段が、許可されたモデルのセキュリティキー、または許可された Android・iOS デバイス上で動作する AGOV Access アプリであること
  • AGOV はリモートフィッシングによるアカウント乗っ取りには強いが、他人の氏名や身元情報を使ってアカウントを作る行為までは、基本登録段階では防げない
  • 追加確認の結果、機微と分類されるサービスにアクセスする際には 身元確認が必要 である
    • 登録直後の時点では、まだそのようなサービスにはアクセスしていなかった
  • サポートフォームからバグを報告したが、返答はなかった

設計から得られる教訓

  • 堅牢ではない入力とサポート設計

    • 6つの入力フィールドと複雑なJavaScriptロジックで派手なUIを作る方式は堅牢ではない
    • ネイティブのブラウザ入力フィールド1つを使い、CSSで見た目だけ整えるほうがよい
    • フォームロジックをDOMから完全に分離すると、送信時にDOM値を読めず、上級利用者やブラウザ拡張も入力値を修正できない
    • サポートチャネルが1つしかないと、そのチャネル自体が壊れたときに連絡手段が消えるため、メールや電話のような 第2のチャネル が必要になる
    • 本体製品とサポートチャネルに同じログインロジックを適用すると、ログインできない利用者からのバグ報告を取りこぼす
    • 多くのインターネットサービスがステータスページを別ドメインに置くのも同じ理由である
    • サポート依頼をメール認証の後ろに置けばスパムは減らせるが、実際の利用者による障害報告も同時に減ってしまう
    • 一部システムで利用を義務化した後になってソースコードを公開すると、利用者の問題解決やバグ・回避方法の共有が制限される
    • 法的なソース公開は、単なるコンプライアンス項目を超えて、実際の障害調査にも有用である
  • 調査に役立ったオープンWeb

    • 関連するJavaScriptロジックが難読化されていなかったため、数回の文字列検索だけで原因を見つけられた
    • 高度なデコンパイラや高価なLLMは必要なかった
    • コードが難読化されていたり WebAssembly にコンパイルされていたりすれば、原因特定にははるかに多くの時間がかかったはずである
    • コードが承認済みのクローズドOSにしか配布されていなかったのではなく、オープンWeb で提供されていたため、最小化された形でもダウンロードして調査できた
  • 原因究明を可能にした条件

    • 異なるキーボード配列を使う複数のノートPCを比較することで、ブラウザ固有の問題やネットワーク問題の可能性を早い段階で下げられた
    • ただし、利用者の操作ミスではないと確認するため、各ブラウザ組み合わせの画面を体系的にキャプチャする必要があり、初期証拠の収集には時間がかかった

1件のコメント

 
GN⁺ 3 시간 전
Lobste.rs のコメント
  • インターネット上で特に嫌いな慣行の一つは、Webサイトがキー入力を横取りして勝手に処理し、ブラウザにそのまま渡さないことです
    パスワード欄でコピー&ペーストを禁止するサイトのほうがずっと一般的ですが、これも同じ類で、もしかするとさらに深刻に見えます

    • ブラウザがHTMLとCSSだけで標準サポートしている機能を、わざわざJavaScriptで再実装する理由が分かりません
      たいてい無理のある理由で複雑さだけが増えます
  • 文字ごとに入力欄を1つずつ置く方式は伝染病のようなものです
    ある企業が見栄えがいいとして導入したら、今では誰もがそれぞれ自分のバージョンを実装しています

    • こうしたUIがワンタイムコード入力の成功率を実際に高めていたとしても驚きません
      ユーザーが細かく読まなくても、パスワード入力欄ではないことにすぐ気づけるからです
  • 関連事例として、Medium once had a bug like this which prevented entering the character 'Ś'があります

  • 別の実装方法も考えてみましたが、結局、普通の**<input type="text" />より良い方法**は見つかりませんでした
    一般的な入力欄とあまりに違って見えるCSS装飾でさえためらいます。入力値を大きなマスに複製して表示することもできますが、ユーザーが許容長を超えて入力したらどうするかなど、あらゆる問題に答えなければならず、その手間に見合うほどの利点は大きくありません。せいぜい入力欄のフォントとサイズを変えるくらいにします

    • OTPで最もよくあるミスは、1234の代わりに1224のように数字を1つ打ち間違えることです
      2つ目の2をクリックして3を押すのが最速の修正方法ですが、HTML+CSSだけでは実装できません
    • かなりばかげた発想だとは認めますが、1つの**<input type="text" pattern="[0-9]+" minlength=6 maxlength=6>に特殊な字間を適用して、視覚的に6つのマスのように見せられるのか気になります
      より簡単な方法は、ユーザーが単一のテキスト入力欄に入力すると、JavaScriptがDOMの別の場所に6つのマスを描画し、キーを押すたびに発生するinputイベントで更新することです
      どちらがアクセシビリティ上より良いかは判断が難しいです。スクリーンリーダーの観点では、普通の<input>と装飾用の<canvas alt="">を併用する後者のほうがむしろ良いかもしれず、キーボード操作もおかしくなりません。しかしスクリーンリーダー以外の支援手段を使うユーザーにとっては、<input>を視覚的に隠すことが
      アクセシビリティ上の大惨事**になり得るため、非常に慎重になります
  • 多くのサービスのように、メールにコードと有効化リンクを併記してもよいでしょう

    コード入力: XX XX XX
    
    またはリンクをクリック: <リダイレクト + コード有効化リンク>  
    

    これでも同じ問題を解決できます

    • メール内のリンクをクリックするようユーザーを慣らすのは、フィッシング対策を後退させることだと思います
    • ノートPCでWebサイトを開き、携帯電話でメールを受け取った場合、コピー&ペーストはそれほど便利ではありません
      それを例外的な状況だと考えることもできますが、アクセシビリティとはまさにそうした例外をきちんと扱うことです
  • Tridactylにもまったく同じ問題があります https://github.com/tridactyl/tridactyl/issues/3257
    2019年に初めて報告されましたが、今回の件をきっかけに、恥ずかしさからでも修正されるかもしれません

  • ただ動くWebサイトよりも、壊れている、または遅いWebサイトを作るほうがはるかに多くの労力がかかるという事実を、またも示しています