エンジニアであれば居酒屋やファミレスの注文用タブレットを使ってみて「API定義どうなってるんだろう」とか「メニュー表示されるの遅いなぁ、計測してないけど頻繁に変わるものじゃないんだから端末で長めにキャッシュさせておけば良いのに」などと考えたことのある方もいるでしょう。
さて、読者の8割がブラウザバックしたところで、今回は最近通っているchocoZAPというお手頃価格のジムでの利用者認証の仕組みが気になったので自分なりの仮説をChatGPTで検証してみたプロセスを書いていきたいと思います。
流れ
ここでいう認証システムについて説明しておきます。chocoZAPでは各店舗(ジム)に受付などの従業員は一切存在しません。利用者は専用のchocoZAPアプリをスマホにダウンロードしており、アプリに入館証であるQRコードを表示させます。そのQRコードを入口にある読み取り装置にかざすと認証が行われ、成功すれば解錠され入館できるという仕組みです。
仮説
実際にどのような認証システムの設計であれば良さそうでしょうか。ここでは以下のポイントを重視して考えます。
- chocoZAPアプリからの認証リクエストであることを担保する。QRコードは本当に正規のアプリで表示されているのか?
- chocoZAPの会員であることを担保する。これは当然。
- QRコードが漏洩しても問題ない設計になっているかどうか。これも大切ですね。
これを満たすには以下の手順が良いのかなと思いました。
- 利用者は予めchocoZAPアプリにログインしておく。以降、アプリは端末にユーザー情報を保持する。
- chocoZAPアプリで現在時刻とユーザー情報をもとにトークン(文字列)を生成。トークンをQRコードに埋め込む。
- 読み取り装置はQRコードを読み取ったら、認証サーバにトークンを送付する
- 認証サーバはトークンをデコードし、ユーザー認証と時刻が直近であるかどうかを確認し結果を返す
- 読み取り装置はその結果に従い、扉を解錠する。
要するにアプリ側でトークンを発行するということですね。ポイント2と3はクリアしています。しかしこれだとポイント1の「chocoZAPアプリからの認証リクエストである」かどうか分かりません。
ですが本当にchocoZAPアプリからの認証かどうか確かめる必要があるのでしょうか?私はよく分からなくなってきました。また、トークン生成のロジックをアプリ側に持たせるのも何だか怖い気がします。
ChatGPTで答え合わせ
Q. Chocozapというジムでは利用者が専用のアプリからQRコードをジムの入り口にある専用の読み取り装置に読み取らせて認証されると入場できます。この時、アプリとサーバの間で行われている具体的な認証の仕組みを提案してください。
A.
-
アプリの認証: 利用者が初めてアプリを使用するときに、自分の身分を証明するために必要な情報(例えばメールアドレス、パスワード、あるいはソーシャルログインなど)を用いて認証を行います。アプリはサーバと通信し、利用者の認証情報を検証します。
-
QRコードの生成: 認証が成功すると、アプリはサーバから一意のトークン(通常は暗号化されていて、ユーザーの情報や現在の日時などを含む)を受け取ります。その後、アプリはこのトークンを基にQRコードを生成します。このQRコードは、利用者がアプリを開くたび、または定期的に(例えば毎分、毎時間)新しく生成されます。
-
QRコードのスキャン: 利用者はアプリを開き、生成されたQRコードをジムの入り口にある専用の読み取り装置に提示します。この装置はQRコードをスキャンし、含まれる情報(一意のトークン)をデコードします。
-
サーバへの認証リクエスト: QRコードの読み取り装置は、デコードされた情報をサーバに送信します。サーバはこの情報(トークン)を用いて、利用者が認証されたかどうかを検証します。
-
認証の結果: サーバは利用者の認証を検証した後、その結果を読み取り装置に送信します。読み取り装置はこれに基づいて行動します。もし認証が成功していれば、利用者をジムに入場させます。認証が失敗した場合は、入場を拒否します。
このアプローチにより、セキュリティを保ちつつ利用者の利便性を向上させることができます。利用者は毎回新しいQRコードが生成されるため、QRコードが盗まれても次回からは無効になります。また、サーバで全ての認証を行うため、読み取り装置がハッキングされても利用者の情報は安全です。
なるほど、トークンの生成をサーバ側に任せるとアプリ側に重要なロジックを持たなくて良いので安心ですね。加えてchocoZAPアプリ以外からのリクエストをケアしなくて良いのか聞いてみました。
A.
ここで、利用者がアプリを通じて生成したQRコード以外のリクエストを識別・排除するために、以下の追加的な検証手順を導入します:
リクエストの元の確認: サーバは、リクエストが本当にアプリから来たものかを確認します。これは、リクエストに含まれる特殊なヘッダーや署名などを確認することで行われます。
トークンの有効性確認: トークンは一定の時間後に無効になるように設定されています(例:数分、数時間など)。これにより、もしトークンが何らかの理由で漏洩しても、その有効期限が切れた時点で利用できなくなります。
なるほど、アプリが特殊なヘッダーを生成するようにするのが一番根本的な対策のように思えます。実際に実装する場合はQRコード内にヘッダーとなる文字列をトークンとは別に埋め込むような実装になるでしょうね。
トークン生成などの重要なロジックはサーバに置く。なるほどという感じでした。