はじめに
こんにちはこんばんは!
昨今、セキュリティへの関心が非常に高まっています。
二段階認証を取り入れる企業が多くなってきました。
最近の例で言うと、Githubが2023年3月ごろに二段階認証を義務化したのは記憶に新しいと思います。
そこで、今回は認証の基礎知識をおさらいした上でTOTPを使った二段階認証の仕組みと導入時の注意点について解説します!
※本記事の内容は、ビアバッシュ(社内の技術共有会)にて登壇発表した内容です。
ビアバッシュの取り組みについては以下の記事を読んでみてください!
基礎知識
認証と聞くと、二段階認証と二要素認証が思い浮かびますね!
この2つはどう区別されるのでしょうか?
二要素認証とは?
以下の要素を2つ以上組み合わせて認証することを二要素認証と言います。
たとえば、ATMでお金を引き出すケースを想定しましょう。
まずATMに本人が持っているカードを差し込む必要があります。このカードが所持情報となります。
次に4桁のパスワードを入力します。このパスワードは記憶情報となります。
ですので、所持情報と記憶情報の二要素認証をクリアして初めてATMでお金を引き出すことができます。
このケースでは二要素ですが、二要素以上ある場合は多要素認証(MFA)とも呼ばれます!
二段階認証とは?
ある画面で認証を行い、さらに別の画面でも認証を行うことを二段階認証といいます。
たとえばログイン画面でパスワードを入力したあと、次の画面で秘密の質問に答えるようなケースです。
このケースでは二段階ですが、3つ以上の段階を踏む場合は多段階認証とも呼ばれます!
二要素認証と二段階認証の違い
二要素認証は認証の要素数について着目し、二段階認証は認証のステップ数について着目しています!
たとえば、ログイン画面でIDとパスワードを入力後、次の画面でメールに届いたワンタイムパスワードを入力するケースは二要素認証/二段階認証どちらに当てはまるでしょうか?
正解は「どちらとも言える」です。
「記憶情報であるパスワード」と、「(メールが届く端末を所持しているという意味で)所持情報であるメールに届いたワンタイムパスワード」の二要素の組み合わせです。
しかも、「IDとパスワード入力画面」と「ワンタイムパスワードの入力画面」の二段階のログインです。
ですので、二要素認証でもあり二段階認証でもあると言えます!
ワンタイムパスワードとは?
一度しか使えない期間限定のパスワードのことをワンタイムパスワードと言います。
OTP(One-Time-Password)と略されます。本記事も以降はOTPとします。
OTPの代表的な生成方法はHOTPとTOTPがあります。
HOTPもしくはTOTPを使って二段階認証を実現するのが基本セオリーです!
HOTPとTOTPについて
いずれもRFCに規定されており、秘密キーを利用してOTPを生成します。
また、OTP生成時はクライアントとサーバで同じ秘密キーを利用します。
秘密キーは認証するクライアントとサーバしか知り得ない情報です。
Githubで二段階認証をする場合、QRコードをGoogleAuthenticatorなどの認証アプリで読み取ると、6桁のOTPが表示されます。
このとき、秘密キーはQRコード化されており、認証アプリがその秘密キーを使って6桁のOTPをHOTP/TOTP方式で生成しています。
Githubでは秘密キーをQRコードで表示しているページのsetup_keyをクリックすると、秘密キーが平文で表示されます。
HOTPとは?
HMACベースのワンタイムパスワードアルゴリズムです。
RFC 4226 - HOTP: An HMAC-Based One-Time Password Algorithm 日本語訳
秘密キーと通算の認証回数からOTPを生成する方法です。
クライアント側は、クライアント側に保持した秘密キーと通算の認証回数を基にOTPを生成してサーバに送信します。
サーバ側は、サーバ側に保持したクライアントと同じ秘密キーと通算の認証回数を基にOTPを生成します。
両者とも、秘密キー・通算の認証回数・生成アルゴリズムが同じ → 同じOTPが生成されるため、認証OKとなります!
秘密キーが同じでも通算の認証回数がクライアントとサーバで異なると、生成されるOTPも異なるため認証に失敗します。
認証が途中で中断されるなどでクライアントとサーバで認証回数のずれが発生することがあります。
また、クライアント側でHOTPを作成するときに認証回数も更新する実装かつ何度もHOTPを再生成する仕様にしてしまうと、認証回数のずれが発生します。
対応策として、認証回数ズレの補正や認証回数リセットの要件検討/実装が必要です。
TOTPとは?
秘密キーと認証時の現在時刻からOTPを生成する方式です。
RFC 6238 - TOTP: Time-Based One-Time Password Algorithm 日本語訳
クライアント側は、クライアント側に保持した秘密キーと現在時刻を基にOTPを生成してサーバに送信します。
サーバ側は、サーバ側に保持したクライアントと同じ秘密キーと現在時刻を基にOTPを生成します。
なお、現在時刻はサーバ側の方が必ず後になってしまうので、OTPには発行されてから30秒間を有効とする猶予期間があります。
両者とも、秘密キー・現在時刻・生成アルゴリズムが同じ → 同じOTPが生成されるため、認証OKとなります!
サーバ側は、30秒毎に有効なOTPを更新します。
ですので、発行されてから認証までに30秒を越えたOTPを送信すると、有効ではないOTPとして判断され認証に失敗します。
例えば、クライアントの端末の時間とサーバ側の時間にズレがある場合は認証に失敗します。
TOTPの時刻ズレ対策
TOTPで生成されたOTPは30秒単位で有効期限を伸ばすことができ、時刻ズレを吸収することができます!
HOTPと比較して認証のズレ対策に柔軟性があり実装コストも低いです。
導入編
では、TOTPを使って二段階認証を導入するために考慮が必要なポイントを抑えていきましょう!
TOTPの時刻ズレ対策の実装
TOTPの生成と認証がセットになったリポジトリはいくつもあります。
今回は、以下のリポジトリのプログラムを参考にしてみます。
PHPGangsta/GoogleAuthenticator.php
のverifyCode
関数を見てみましょう。
PHPDocにもある通り引数$discrepancy
の値を増やすことで認証期限を延長できます。
/** * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now. * * @param string $secret * @param string $code * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) * @param int|null $currentTimeSlice time slice if we want use other that time() * * @return bool */ public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) { if ($currentTimeSlice === null) { $currentTimeSlice = floor(time() / 30); } if (strlen($code) != 6) { return false; } for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); if ($this->timingSafeEquals($calculatedCode, $code)) { return true; } } return false; }
TOTPの注意点
OTPは30秒に1つ生成されます。
たとえば、OTP"555555"が11:59:30、OTP"666666"が12:00:00に生成されたとします。
OTP"555555"の有効期限は11:59:30〜11:59:59の間です。
OTP"666666"の有効期限は12:00:00〜12:00:29の間です。
このとき、12:00:00〜12:00:29の間で有効なOTPは"666666"のみです。
そのため、12:00:00〜12:00:29の間でOTP"555555"を使って認証した場合は失敗します。
ここで、OTPの有効期限を1分に延ばしたとき、OPT"555555"の有効期限は11:59:30〜12:00:29になります。
すると、12:00:00〜12:00:29の間で有効なOTPは"555555"と"666666"の2つになります。
そのため、12:00:00〜12:00:29の間でOTP"555555"を使って認証した場合は成功します。
つまり、OTPの有効期限を延ばすほど有効なOTPが増えます!
その結果、セキュリティレベルを下げてしまいますので以下のような対策を検討する必要があります。
- 総当たり攻撃(ブルートフォース攻撃)対策
- ログイン回数に制限を設ける
- 3回ログインに失敗したらログインできない、などのアカウントロック機能を併用する
- 失敗が続いたときはアクセス元のIPアドレスからのログインを不可とする
- ログイン回数に制限を設ける
- 不正アクセス対策
- ログイン時にメール通知する
- 不正アクセスを判別するためのログを記録する
- OTP不正利用対策
- 認証に利用したOTPを利用不可にする
特に、総当たり攻撃対策は必須と言っても過言ではないでしょう!
まとめ
二段階認証を組み込むことで、セキュリティレベルを向上させることができます。
しかし、なんとなく導入してしまうと思わぬ落とし穴に気づかない可能性があります。
システムに要求される利便性とセキュリティレベルをうまく調整しましょう!