初めまして。Eight事業部のPlatform Unit エンジニアの 李です。
私が属するPlatform Unitは基盤チームとも呼ばれ、Eightをより安心して使えるサービスにするための開発を行っています。 私は主にセキュリティー周りの開発を担当しています。今回は5月末にリリースしたEightの二段階認証機能について、調査や開発をする際にあった学びや気づきを共有できればと思います。
2段階認証機能を開発するにあたって
5月末にEightに二段階認証がリリースされました! これで前より安心して使えるサービスになりましたが、これからの内容は主に調査や開発をする際にあった学びや気づきです。
私は業務上で利用しているサービスではすでに二段階認証を設定していました。と言ったものの中で実際にどんなことが起こっているのかわからず、どこから手を入れればいいのかが全く不明でした。 早速調べてみるとWikipediaさんはこう言っています。
多要素認証は、アクセス権を得るのに必要な本人確認のための要素を複数、ユーザーに要求する認証方式である。 必要な要素が二つの場合は、二要素認証や二段階認証とも呼ばれる。
なるほど。ここで気になる一点 二要素認証
と 二段階認証
はなんの違いがあるのでしょう?
二要素認証
世の中で使用されている認証はその方法も山ほどありますが、大きくは三つの要素に分けられます。
- PWD、PINコードのような
ユーザーだけが知る情報
- 鍵、クレジットカードのような
ユーザーが持っているもの
- 指紋、虹彩のような
ユーザーの生体情報
ID/PWDを使った認証はほとんどのサービスで行われていて、ここでもう一つの要素を加えたのが 二要素認証
になります。
身近な例で例えると、自宅の金庫に貴重品があるとして、鍵を持った人が家には入れても、暗証番号がついている金庫は開けられない。逆に金庫の暗証番号がわかる人でも、鍵がないとそもそも家に入れない。のようなことですかね。
二段階認証
二段階認証は、認証要素は問わず段階を分けて認証を行うことを言います。
長く使われなかったサービスにログインした時に、生年月日、自分が設定した質問などを問われた経験はありませんか?そういったものが一つの例になります。
ID/PWDも質問の答えも ユーザーだけが知る情報
といった性質は同じですが、それを二段階に分けて求めていて 二段階認証
と呼ぶのですね。
二要素を段階的に求めれば、二段階かつ二要素認証である、と整理しても良さそうです。 また二要素であることが、よりセキュリティ的に強固なのは明白なことでしょう。
インタネットサービスでの二段階認証
インタネットサービスの二段階認証は、なんらかの方法で発行したワンタイムパスワードを使うような方法が多く使われています。
そのなんらかの方法には TOTP
と HOTP
というものがあります。
時刻ベースのワンタイムパスワード TOTP: Time Base One Time Password
TOTPはサーバーとクライアントが同じシークレット情報を持った上で、シークレット情報と時間情報の組み合わせでワンタイムパスワードを発行する方法です。
実装
rubyでは rotp というgemを使うと簡単に実装できます。
require 'rotp' require 'rqrcode' class TotpAuthenticator def initialize @secret_key = ::ROTP::Base32.random end def qr_code RQRCode::QRCode.new(provisioning_uri, size: 12) end def valid_otp?(otp) !otp_generato.verify(otp, drift_ahead: 30, # 30秒前のOTPも考慮する drift_behind: 30, # 30秒後のOTPも考慮する at: Time.now).nil? end private attr_reader :secret_key def otp_generator @otp_generator ||= ROTP::TOTP.new(secret_key, issuer: 'issuer', interval: 30) # 30秒ごとにOTPを更新する end def provisioning_uri() otp_generator.provisioning_uri('test@example.com') end end
流れ
二段階認証の設定プロセスは簡単にこうなります。
- サーバーは二段階認証のためのシークレットを発行する
- そしてそのシークレットを元に認証アプリ登録用のURIを生成する
- URIをユーザーが簡単に読み取れる形式にして(ex: QRコード)ユーザーに伝える
- ユーザーはその情報を認証アプリに読み込み、サービスを登録する
また、登録後の認証では
- ユーザーは通常の方法で認証を試みる
- サーバーはユーザーを確認し、ワンタイムパスワードを問う
- ユーザーは認証アプリに表示されたワンタイムパスワードを入力する
- サーバーはシークレットと 現状時刻*1を元にワンタイムパスワードを発行する
- ユーザーのワンタイムパスワードと比較し、認証を行う
どうですか?意外とシンプルですよね。 いろんなサービスで二段階認証を使われているユーザーさんには馴染みのある認証方法だと思います。
カウンターベースのワンタイムパスワード HOTP: HMAC-based One-time Password
HOTPも同じくサーバーとクライアントが同じシークレット情報を持った上、シークレット情報とパスワードの生成回数の組み合わせでワンタイムパスワードを発行する方法です。
サーバーでは認証するたびに生成回数が増加し、それに合わせてユーザーもトークンをリフレッシュしなければなりません。
カウントのずれが発生した場合には同期作業が必要になります。
サービスとしては SMS認証
で利用されることが多いです。
実装
TOTPとほぼ似ていますが、サーバーは生成回数の情報を持ってなければいけません。
require 'rotp' class HotpAuthenticator def initialize @secret_key = ::ROTP::Base32.random @count = 0 end def count_up self.count = self.count + 1 end def valid_otp?(otp) !otp_generator.verify(otp, count).nil? end attr_accessor :secret_key, :count private def otp_generator @otp_generator ||= ROTP::HOTP.new(secret_key) end def provisioning_uri otp_generator.provisioning_uri('test@example.com') end def generate_otp otp_generator.at(count) end end
流れ
登録プロセスはTOTPとそこまで変わりはありません。
ただ認証時には、ユーザーのワンタイムパスワードは自動更新されないという違いがあります。
- ユーザーは通常の方法で認証を試みる
- サーバーはユーザーの確認後、ワンタイムパスワードを問う
- ユーザーは認証アプリで取得したワンタイムパスワードを入力する
- サーバーはシークレットと生成回数を元にワンタイムパスワードを生成する
- ユーザーのワンタイムパスワードと比較して、認証を行う
- サーバーはパスワードの生成回数をカウントアップする
- ユーザーは認証アプリでワンタイムパスワードを再発行(カウントアップ)する
Eightの二段階認証
EightではTOTP方式の 認証アプリを利用する
方法と、 HOTP方式の SMS認証で受け取る
方法を用意しています!
結論
色々話しましたが実は二段階認証の設定には1分もかかりません。より安全にEightを使ってみませんか?