こんにちは、富士榮です。
OpenID Providerを作っていくとメインの機能ではないけど必要な属性情報を扱う必要性に気がついてきます。
例えば、最終ログイン日付とか利用しているOSやブラウザの種別などの環境要素なども代表的なものの一つだと思います。
これらの情報をどうやってユーザデータベースに保存するか、そしてこれらの情報をどのタイミングで読み書きするのか、がID基盤の性能やセキュリティ、スケーラビリティなどのいわゆる非機能系の設計を行う上では重要な要素になりそうです。今回はそれらの属性の取り扱いを含むログイン処理の実装について考えてみたいと思います。
非機能系で利用できそうな属性とは?
ちょっとこの辺を考えてみましょう。
例えばこんな区分の属性が考えられるかもしれません。
通常のメールアドレスや名前などのユーザ情報とは異なりますが、ユーザを認証する際のなりすましリスクの判定やトラブル時の原因解析などを考えるとこれらの情報は重要になる場面が容易に想定できます。(もちろんこれらの情報以外にも利用できる属性はたくさんあると思います)
これらの属性情報の扱いを含めログイン処理はどのように実装するべきか?
では、単純に上記の情報をアクセスの都度ユーザデータベースに保存していけば良いのか?というとこれはこれで考えるポイントがありそうです。
一般に認証(ログイン)処理は出来うる限りシンプルにしていくことが望ましいと考えられます。これは可用性や性能の観点から複雑な処理を入れるとログイン処理が遅くなったり同時に多数のユーザがアクセスする場合にサーバーリソースを多く消費してしまうことが考えられるためです。
ちなみにこれはMicrosoft Azure AD B2Cの例ですが、ユーザデータベースから属性情報を取得するオペレーションとユーザデータベースへ属性を書き込むオペレーションでは処理にかかる時間が倍以上異なる(当然書き込みの方が倍以上遅い)ということが(あくまで個人の経験として)わかっています。これは他の処理系を使っても同様の傾向にあるはずです。
また、加えてログイン時の条件や認証結果によってログイン処理にかかる時間があまりに異なると内部処理の推測をされてしまうなど攻撃者によって大きなヒントを与えてしまうことにもなりえます。(例えば、削除済みユーザ、ロック済みユーザとパスワードを単に間違えたユーザで認証試行に対するレスポンス時間が極端に異なると、ブルートフォースアタックをされた時にユーザが存在する可能性がわかってしまう、などのリスクに繋がります)
そう考えると、少なくともログイン処理では、入力された識別子をキーにデータベースを検索したあと、
- 以下の処理の時間を同じくらいの時間がかかるようにウェイトをかける
- エントリが存在しなかった場合
- エントリが存在するが
- 提供されたクレデンシャルで認証ができなかった場合
- ロックアウトなどにより認証をブロックする場
- ログイン成功の場合は環境属性を非同期で書き込む(キューインフチェーンパターンなどの利用)
など実装上の工夫をするべきだと言えると思います。
スクラッチでOpenID Providerを作って商用で利用することはそれほどないとは思いますが、この辺りのことも考えて実装されたID基盤を使うことでセキュアでスケーラビリティの高いシステムを構築することができるようになると思います。
0 件のコメント:
コメントを投稿