Perlでワンタイムパスワード認証を導入するために使えそうなライブラリ
概要
最近流行の不正ログインへの対策なんかで、ワンタイムパスワード(以下OTP)の導入がはやっている。
ユーザーがとても使いにくい点など、OTP認証についての議論はおいておいて、既存の認証のしくみに加えOTP認証を実装しようと思ったらどんな感じになるかを考え、実現するために必要そうなライブラリを用意した話。
ここでいうOTP関連の仕様
もう一個関係ありそうなのがあるがこれは今回の話の対象外。
OTPの使いどころ
例えば、このように使われているのを見かけることがある。
- 通常のパスワード認証 + OTP入力 : Googleなど
- ユーザーが設定したPIN + OTPによる認証 : ハードウェアトークンを用いるSSL-VPN系のプロダクトとか
- 10個の(一見ランダムな)数字を保存しておいて一回ずつ利用 : Googleのバックアップコードみたいなやつ
ここではこれらの方式が素晴らしく、導入すべきだという話ではない。
3つめはいくらでも簡単に実装できるので微妙だが、HOTP/TOTPの利用という観点からこれらの処理を細かく見ていく。
パスワード認証 + OTP認証
- Google Authenticator的なものにOTPの設定をする
- OTP設定作成(TOTP)、DBなどに保存
- 設定用URIを生成
- QRコードなどで読み込ませる
- パスワード認証成功後、OTP入力を求め、入力されたOTPを検証
- ユーザーに紐づくOTP設定をDBなどから読み込み
- OTPの値を計算して比較
PIN + OTP認証
- Google Authenticator的なものにOTPの設定をする
- OTP設定作成(TOTP)、DBなどに保存
- 設定用URIを生成
- QRコードなどで読み込ませる
- PIN + OTP入力を求め、入力されたOTPを検証
- ユーザーに紐づくOTP設定をDBなどから読み込み
- OTPの値を検証
PIN + OTPの値は生で送っていいのか...とかは今回割愛する。
結局パスワードがPINになって一緒に送られるかどうかの話。
10個の(一見ランダムな)数字を保存しておいて一回ずつ利用
- 数字の払い出し
- OTP設定作成(HOTP)、DBなどに保存
- カウンタに用いる値を10個作成(連番でもランダムでも良い)、DBなどに保存
- それぞれのOTPの値を払い出す
- OTPを受け取って検証
- ユーザーに紐づくOTP設定をDBなどから読み込み
- カウンタに用いる値(で有効なもの)をDBなどから読み込み
- それぞれの値でOTPを生成し、それに含まれていたらそのカウンタの値を削除
必要な機能
Google Authenticattorへの設定用URLは仕様が公開されているのでそれにしたがって作成すれば良い。
KeyUriFormat - google-authenticator
OTPの値の計算については、Authen::OATH が使える。
あとはOTP設定の作成/保存と読み込みあたりを簡単にするしくみがあれば捗りそう。
データストア/既存のユーザーとの紐付けの部分はサービス独自なものであるので、後述するような"薄い"内部用HTTPサーバーのためのライブラリを検討した。
作ったもの
Authen::OATH::KeyURI
Google AuthenticatorにHOTP/TOTPの設定を行うときに読み込ませるURIを生成するモジュール。
生成したURIをリンク/QRコードなどで読み込ませることで設定が可能。
Net::OATH::Server
(今のところは)InternalなOTP認証用HTTP Serverのための"薄い"PSGIアプリケーションを提供しているモジュール。
- このモジュールがやること
- OTP設定のCRUD処理の"インターフェース"の提供
- OTP設定のCRUD処理とOTPの検証(Login)処理のPSGIアプリケーションを提供
- 利用者がやること
- インターフェースに従ってOTP設定のCRUD処理を"実装"
- 提供されているPSGIアプリケーションを内部向けHTTP Serverとして動かす
- Create機能で取得したOTP設定のidをユーザーに紐付け
- Create機能で取得したOTP設定とAuthen::OATH::KeyURIからユーザー設定用URLを生成
- OTP検証時にLogin機能を利用
- 不要になったらDelete
- 細かいパラメータを利用する場合はUpdate
これらを使うと、内部向けにHTTP Serverを立てて利用することでOTP認証を簡単に実装できそう。
さすがにわざわざHTTP Serverたてるのもあれだなって言われそうなので、OTP設定のCRUD/Login部分をPSGIアプリケーションと切り離して、モジュールにして密結合な利用もできるようにする予定。
ちなみに、これをプロダクション環境で使う予定とかは今のところない。