何かとセキュリティが問われる昨今、これまでのIDとパスワードだけの認証では不安を感じている方も多々いると思います。
近年、もしかしたら皆さんもご存知か、既に使われているかもしれませんが、ワンタイムパスワードを利用した2段階認証というものがあり、今回はそれをLaravelに導入していきます。
2段階認証とは?
2段階認証は、さまざまなオンラインサービスで使われています。
引用:2段階認証とは何か?どんなアカウントで使うべきか? – カスペルスキー公式ブログ
ユーザーに2種類の認証情報を要求することにより、アカウントへログインする際のセキュリティを強化するのが目的です。
1段階目で要求される認証情報は、パスワードであることが一般的です。
2段階目で要求される認証情報はさまざまですが、最も一般的なのは、SMSかメールで送られてくるコードです。
2段階認証の根底にあるのは、ログインするには何かを「知って」いて、何かを「持って」いなければならない、という論理です。
2段階認証システムを導入する方法
SMSを送信するシステムだとお金かかるし、メールが溜まるのも嫌なので、使用するパッケージは「Google Two-Factor Authentication(以下、Google2FA)for Laravel」を採用しました。
何より、あのGoogle先生が提供しているので何かと安心です。
今回はそのモジュールをサーバー側に組み込み、iOSやAndroidで無料でダウンロードできアプリ「Google Authenticator」を利用して実装します。
パッケージのインストール
それでは、いつのようにComposerを使用してパッケージをインストールしましょう。
これが2段階認証を実装してくれるGoogle2FAのパッケージです。
1 |
composer require pragmarx/google2fa-laravel |
次にQRコードを作成してくれるパッケージも入れておきます。
これはなくてもいいですが、ユーザーがアプリで設定する際、非常に便利になりますので、ぜひインストールしておきましょう。
1 |
composer require bacon/bacon-qr-code |
※Laravel 5.5は他に何もする必要はありませんが、Laravel 5.4以下はconfig/app.phpのprovidersとaliasesに追加してください。
最後に設定ファイルを公開して完了です。
1 |
php artisan vendor:publish --provider="PragmaRXGoogle2FALaravelServiceProvider" |
フィールド追加とモデル更新
Google2FAで必要になるシークレットキーを保存するためのフィールド(カラム)を追加します。
基本的には自由ですが、私は認証で使用するためusersテーブルに追加しています。
マイグレーションファイルを作って、
1 |
php artisan migrate:make update_users_table |
中はこんな感じです。
database/migrations/XXXX_XX_XX_XXXXXX_update_users_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
... class UpdateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->text('google2fa_secret')->nullable()->after('password'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function(Blueprint $table) { $table->dropColumn('google2fa_secret'); }); } } |
フィールド名は設定ファイルconfig/google2fa.phpのデフォルトに合わせて、google2fa_secretとしています。
できたら、
1 |
php artisan migrate |
でusersテーブルに反映させましょう。また、同時にUserモデルも変更しておきましょう。
$fillableとパスワード同様に$hiddenにも加えておきます。
app/User.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', 'google2fa_secret', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'google2fa_secret', 'remember_token', ]; ... |
シークレットキーの暗号化
余談ですが、シークレットキーを暗号化することもお勧めします。
以前の記事で紹介したトレイトを利用する方法でも構いません。
単純に暗号化するならこちらを参考にどうぞ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... /** * Ecrypt the user's google_2fa secret. * * @param string $value * @return string */ public function setGoogle2faSecretAttribute($value) { $this->attributes['google2fa_secret'] = encrypt($value); } /** * Decrypt the user's google_2fa secret. * * @param string $value * @return string */ public function getGoogle2faSecretAttribute($value) { return decrypt($value); } } |
シークレットキーの登録
ユーザー登録する際に、一緒にシークレットキーを生成して、先程作成したgoogle2fa_secretフィールドに追加します。
RegisterController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
... use Google2FA; ... /** * Create a new user instance after a valid registration. * * @param array $data * @return AppUser */ protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => bcrypt($data['password']), 'google2fa_secret' => Google2FA::generateSecretKey(), ]); } } |
追加したのはこの一行だけです。
1 |
'google2fa_secret' => Google2FA::generateSecretKey(), |
これでシークレットキーの登録は完了です。
ミドルウェアを追加
Google2FAのパッケージには既にミドルウェアが用意されています。
それを反映させるためにKernel.phpに追加しましょう。
app/Kernel.php
1 2 3 4 5 6 7 8 9 10 11 |
/** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ ... '2fa' => PragmaRXGoogle2FALaravelMiddleware::class, ]; |
通常、ミドルウェアでログイン状態をフィルターする場合は’auth‘だけですが、2段階認証をするには上記で追加した’2fa‘を加えます。
routes/web.php
1 2 3 |
Route::group(['middleware' => ['auth', '2fa']], function () { ... }); |
更に、認証画面用のルートも用意されています。
1 |
Route::post('/2fa', function () {return redirect()->back();})->name('2fa'); |
ビューを追加
ビューを作成する前に、Google2FAで用意されているgetQRCodeGoogleUrl関数でQRコードのURLを作成します。
1 2 3 4 5 6 7 8 9 |
... use Google2FA; ... $google2fa_url = Google2FA::getQRCodeGoogleUrl( config('app.name'), auth()->user()->email, auth()->user()->google2fa_secret )); ... |
$google2fa_url変数に代入したものを次のビューに渡してあげましょう。
ビューも設定ファイルに記載している通り、’google2fa.index‘に作成します。
resources/views/google2fa/index.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
... <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> 2段階認証 <small>Two Factor Authentication</small> </h1> </section> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-md-6 col-sm-6 col-xs-12"> <div class="panel panel-info"> <div class="panel-header with-border"> <h3 class="panel-title">QRコード</h3> </div> <div class="panel-body text-center"> <img src="{{ $google2fa_url }}" alt="" style="max-width: 100%;"> </div> </div> <div class="panel panel-info"> <div class="panel-header with-border"> <h3 class="panel-title">シークレットキー</h3> </div> <div class="panel-body text-center"> <div> <div class="input-group input-group-lg"> <span class="input-group-addon"><i class="fa fa-key"></i></span> <input id="copy" type="text" class="form-control" value="{{ auth()->user()->google2fa_secret }}" readonly> <span id="clipboard" class="input-group-addon" data-clipboard-target="#copy" data-toggle="tooltip" title="クリップボードにコピー"><i class="fa fa-clipboard"></i></span> </div> </div> </div> </div> </div> <div class="col-md-6 col-sm-6 col-xs-12"> <div class="panel panel-info"> <div class="panel-header with-border"> <h3 class="panel-title">ワンタイムキーで認証する</h3> </div> <div class="panel-body"> <form class="form-horizontal" method="POST" action="{{ route('2fa') }}"> {{ csrf_field() }} <div class="input-group input-group-lg"> <input id="one_time_password" type="number" class="form-control" name="one_time_password" placeholder="123456" required> <span class="input-group-btn"> <button type="submit" class="btn btn-info"><i class="fa fa-check"></i> 認証</button> </span> </div> </form> </div> </div> </div> </div> </section> </div> ... |
ここでのポイントは、QRコードの画像URLを
1 |
{{ $google2fa_url }} |
で取得し、シークレットキーをユーザー登録時に追加した
1 |
{{ auth()->user()->google2fa_secret }} |
で取得します。
そして、ワンタイムキーを入力するフォームの名前を設定ファイルの通り
1 |
one_time_password |
とします。
アプリを使ってみる
ここまで設定したら、Google Authenticatorをダウンロードしてみよう。
ダウンロードはこちら。
Google Authenticatorについて
Google AuthenticatorとはGoogleが開発した二段階認証(二要素認証)を行うトークンソフトウェアである。
引用:Google Authenticator – Wikipedia
AuthenticatorはユーザーがGoogleのサービスにログインする時に必要な通常のIDとパスワードと共に入力しなければいけない6桁の数字コードを提供する。
また、LastPassやDropboxといった他社製のアプリケーションでもコードを発行することが出来る。
今回はかなり長い記事になりました…。
書き洩れてるかもしれませんので、見つけたらご指摘ください。
コメント
コメント一覧 (1件)
[…] WinAuthを導入する前に、対象のサービスのシークレットキー、もしくは読み取れる場合はQRコードを用意しておきましょう。 例えば、以前ご紹介したLaravelでGoogle Authenticatorを利用した2段階認証システムを構築したことがあります。 Laravel 5.5に2段階認証システムを導入する(Google2FA)https://minory.org/laravel-google2fa.html何かとセキュリティが問われる昨今、これまでのIDとパスワードだけの認証では不安を感じている方も多々いると思います。近年、もしかしたら皆さんもご存知か、既に使われているかもしれませんが、ワンタイムパスワードを利用した2段階認証というものがあり、今回はそ… Minory 1 share 1 user この場合だと、2段階認証を設定する場面で表示されるシークレットキーとQRコードです。 他のサービスも同じように表示されるはずです。 このシークレットキーは再発行が難しく、サービスによっては再発行してくれません。 さらに、Google Authenticatorは機種変する際のバックアップ、またはデータ移行ができないようですので、必ず控えておきましょう。 スマホで登録するとこのようになります。 おそらく、PCではURLスキームを使えないです。 URLスキームでスマホのWEBブラウザからアプリを起動するhttps://minory.org/url-scheme.htmlスマホ用のWEBアプリ作ることが増えた今日この頃。ネイティブアプリと連携したくなることもしばしばあります。以前から、メールアプリを開いて指定した宛先に送信させたい場合は「mailto:~」、電話アプリから指定した電話番号へ発信させたい場合は「tel:~」と言う… Minory 2 shares 2 users […]