開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. Laravel >
  5. Laravel Google2FA 登録時 認証コード 2回 [検索]
no-image

Laravel Google2FA 登録時 認証コード 2回 [検索]

こんにちは、さわちゃんです。

今日はLaravelでGoogle2FAを用いて、二要素認証を行うとき、連続する2つの6桁の数字を入力して認証!みたいな実装の実現方法について伝授します。

環境・使用ライブラリ

Laravel Framework 6.18.10
PHP: 7.4.20
antonioribeiro/google2fa: 8.0.0  https://github.com/antonioribeiro/google2fa
endroid/qr-code: 4.0.0 https://github.com/endroid/qr-code

antonioribeiro/google2faによるとPHP8.0でも動くみたいですね。

一応LaravelはAPIとして使用し(バックエンド)、フロントエンドはなにか適当なもの(ReactとかVueとか)という構成を想定しています。

なお、endroid/qr-codeはシークレットキーとアカウント情報を含むURLをQRコード化するのに使用しますが、別のライブラリでもOKです。

 

基礎知識編

 
1. 二要素認証とは?
2. antonioribeiro/google2fa導入時のユーザーの操作手順
3. antonioribeiro/google2faの基本的な導入方法

の3本立てでお送りします。
 

二要素認証とは?

いわゆる認証の三要素のうち、2つを利用するものですね。
認証の三要素とは以下の3つです。
  • 知識
  • 所有
  • 生体
基本的な認証としてID・パスワード認証があると思いますが、あれは知識の部類です。

今回導入するGoogle2FAは、Googleパイセンが提供しているGoogle認証システム(Google Authenticator)をインストールしておき、そのアプリで表示される数字を入力することで認証されるという代物ですが、これは所有の部類にあたるものですね。

なので、ID・パスワード認証の後に、Google2FAでの認証を行えば、知識+所有の2要素を利用することになるので、二要素認証となります。

また、よく混同されがちなのが二段階認証です。
二段階認証とは要素の数を問わず、認証の段階を2つ踏むものを指します。
例えば、ID・パスワード認証の後に、秘密の質問を答える、といったものですね。
これはどちらとも知識の部類にあたるものなので、二要素ではないですが、段階は2つ踏んでいるので二段階認証になります。


表記揺れがアレなので、ここからは二要素認証という言葉で統一していきたいと思います。

 

antonioribeiro/google2fa導入時のユーザーの操作手順

まず大前提として、Google2FAの利用ユーザーはお手元のスマホ・タブレットにGoogle認証システムのアプリをインストールする必要があります。
(App StoreとかGoogle Playにあります)


Android: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2
iOS: https://apps.apple.com/jp/app/google-authenticator/id388497605


そして次に、このGoogle2FAでの二要素認証を利用する前の時点で、Google認証システム上でアカウントを登録しておく必要があります。

なので例えばGoogleの二要素認証の設定でも、自分がログインしている状態で二要素認証をONにする設定を行い、以降のログインにおいては二要素認証が必要になるという形になっているかと思います。

ということで、ユーザーが利用する際は以下のステップを踏んで初めて二要素認証を有効にできます。

1. まずログインする(ID・パスワード認証など)
2. Google認証システムにアカウントを登録する
3. 以降、ログアウトして再びログインする際に二要素認証が求められる

なお、今回は2についての記事です。(二要素認証を利用する前のアカウント登録のお話)
3についてはまたどこかで書きたいと思います。
 

antonioribeiro/google2faの基本的な導入方法

基本的なことはライブラリのREADMEに書いてあるのでさらっと流していきます。

antonioribeiro/google2faを用いたGoogle認証システムへのアカウント登録の大まかな流れとしては以下です。

1. [バックエンド]シークレットキーを発行
2. [バックエンド]シークレットキーと、シークレットキーとアカウント情報を含むURLをQRコードにしたものをフロントエンドへ送る
3. [フロントエンド]シークレットキーとQRコードを受け取り、表示
4. [利用ユーザー]QRコードをGoogle認証システムで読み取る
5. [利用ユーザー]Google認証システム上で表示される6桁の数字を入力する
6. [フロントエンド]シークレットキーと一緒に6桁の数字をバックエンドへ送る
7. [バックエンド]シークレットキーと6桁の数字を元に認証
8. [バックエンド]ユーザー情報にシークレットキーを保存

 

流れ1~2

1. [バックエンド]シークレットキーを発行
2. [バックエンド]シークレットキーと、シークレットキーとアカウント情報を含むURLをQRコードにしたものをフロントエンドへ送る


一本APIを生やして、フロントエンドからコール→バックエンドで生成→フロントエンドに返す、みたいなイメージです。

シークレットキーの発行は簡単です。
 
use PragmaRX\Google2FA\Google2FA;
    
$google2fa = new Google2FA();
    
return $google2fa->generateSecretKey();

これだけです。

続いてQRコード化ですが、まず以下の作業が必要です。

$g2faUrl = $google2fa->getQRCodeUrl(
    '組織名',
    'アカウント名,
    $google2fa->generateSecretKey()
);
組織名とアカウント名はGoogle認証システムに表示されるもので、以下のようになります。
 
組織名(アカウント名)
アカウント名にはユーザーのメールアドレスなんかを入れるのが一般的っぽいですね。

そうしたら上記で作成した$g2faUrlをQRコード化して完了です。
これを忘れてシークレットキーだけをQRコード化してもうまくいかないので注意です。(ハマリポイント)
 

流れ3~6

3. [フロントエンド]シークレットキーとQRコードを受け取り、表示
4. [利用ユーザー]QRコードをGoogle認証システムで読み取る
5. [利用ユーザー]Google認証システム上で表示される6桁の数字を入力する
6. [フロントエンド]シークレットキーと一緒に6桁の数字をバックエンドへ送る

これはもう文字通りなので割愛します。
Google認証システムへのアカウント登録と聞くと、なにか登録画面で色々入力する必要があるのではないかと思ってしまいますが、Google認証システムのQRコード読み取り機能で読み取るだけでアカウント登録は完了します。

登録後、30秒ごとに6桁の認証コードが表示されるので、そちらをフロントエンドで入力させて、シークレットキーと一緒にバックエンドに送信しましょう。
 

流れ7~8

7. [バックエンド]シークレットキーと6桁の数字を元に認証
8. [バックエンド]ユーザー情報にシークレットキーを保存

認証もとても簡単です。
以下のような形でGoogle2FA::verifyKey()にシークレットキーと6桁の認証コードを入れてあげたら認証してくれます。
 
$code = $request->input('code');
$secret = $request->input('secret');

$valid = $google2fa->verifyKey($secret, $code);
なお、Google2FA::verifyKey()は返り値がboolで、もし認証に失敗したとしても例外は発生しません。(ハマリポイント)

なので、
 
$valid = $google2fa->verifyKey($secret, $code);

if (!$valid) {
  throw new 認証コードが不適切Exception();
}
のような感じで自前で例外に変換してあげるとGoodですね。

なお、認証成功時にはそのユーザーが二要素認証をONにしているかどうかを記録するため、シークレットキーを保存してあげる必要があります。
例えばusersテーブルがあるとしたら、google2fa_secretみたいなカラムをもたせてあげればよさそうですね。

 

連続する2つの6桁の認証コードを用いて認証する方法


さて、ここからが本題です。
結構いろんなサービスで、2回連続で認証コードを入力させるものを見かけるのですが、あんまりメジャーではないのでしょうか?
(antonioribeiro/google2faでの実現方法を調べていたのですが、なかなか見つからなかったので・・・)

この記事が世界初の解説記事になるかもしれないと思うと夜しか眠れませんね。

まず実装にあたって、antonioribeiro/google2faの「windowという概念を理解する必要があります。

windowとは、(時間的に)前後どこまでの認証コードを許可するかを司るものです。

例えば、認証コードが以下のように仕組まれているとします。
 
以下の認証コードが30秒ごとに流れていく

111111
222222
333333
444444
555555
666666
777777
888888
999999

もし、現在時点でGoogle認証システムに555555が表示されているとして、windowを0と設定していた場合、555555のみしか認証はできません。
しかし、ここでもしwindowを1とセットすると555555の前後である444444と666666も認証できるようになります。
windowを2とすると333333と777777もOK、といった具合で、windowの数を増やせば増やすほど、認証可能となる認証コードの時間的幅が広がっていくイメージです。

さて、windowの概念が理解できたらもうあとは簡単です。

2つの連続する認証コードを許可するということで、windowは1にセットします。
こうすることで現在表示されている認証コードの前後の認証コードもOKになります。
あとはフロントエンドから2つ認証コードの入力を受け取れるようにしておいて、それぞれが一致しないようにしつつ、2回Google2FA::verifyKey()をしてあげればOKです。

コードにするとこんな感じです。
use PragmaRX\Google2FA\Google2FA;
    
$google2fa = new Google2FA();

// デフォルトが1なのでわざわざ指定しなくてもOKです
$google2fa->setWindow(1);

$secret = $request->input('secret');
$code1 = $request->input('code1');
$code2 = $request->input('code2');

// LaravelのFormRequestのバリデーションで弾いてもOKです(というかそっちの方が楽)
if ($code1 === $code2) {
  throw new 同じ認証コードはダメよダメダメException();
}

$valid1 = $google2fa->verifyKey($secret, $code1);
if (!$valid1) {
  throw new 認証コードが不適切Exception();
}

$valid2 = $google2fa->verifyKey($secret, $code2);
if (!$valid2) {
  throw new 認証コードが不適切Exception();
}
    
// 以降、認証後の処理
// $secretをユーザーと紐付けて保存しておきましょう
これでGoogle認証システムのアカウント登録は完了です。

二要素認証を実装する際は、ID・パスワード認証のあとに6桁の認証コードを入力させるようにし、ユーザーに紐付いているシークレットキーと認証コードでGoogle2FA::verifyKey()する、みたいな流れになります。
 

終わりに

まぁそういわれてみればそうだなというか、windowの概念さえ分かってしまえばそんなに難しくはないですね。
ただ結構ドキュメントが少なく、ライブラリの奥の方まで見ないとなかなか理解しづらい部分が多かった思い出があります。
みなさんもできたらライブラリの実装を一通り見ておくと、より理解が深まるかと思います。

この記事参考になったよ!という方はNextatの公式TwitterまでぜひDMを送ってくださいね!!!!!
(さわちゃんの給料が上がるかもしれません!!!!!)

https://twitter.com/nextat2

それでは〜〜〜
 
TOPに戻る