LaravelでWebAuthnを使った2段階認証を実装する

さてさて、この間【Python】Pasori RC-S380で入退室システムをつくるという記事の中で、非接触型カードリーダーのPasoriamazonでポチってしまったことを書きました。

私はそれほど衝動買いする方ではないのですが、正直なところプログラムのために新しいガジェットを買って試してみるというのが予想以上に楽しい経験でした。

そして、今回の記事も同じようなものです。

業務で必要なものをamazonで購入していたのですが、いろいろ見ているうちにある商品が売っていることに気がついてしまいました。

それが、この「Security Key by Yubico」です(3,600円😅)。

「一体これは何に使うの??」と思われたかもしれませんが、これは今後ログイン認証のメインになるだろうと言われている技術、「WebAuthn※」のためのセキュリティキーで、これがあれば超強力な認証システムが構築できるんですね。

※ Youtube動画を見る限り読み方は「うぇぶおーせん」と言っているようです。

ということで、今回は衝動買いプログラムの第二段として「WebAuthn」で2段階ログインを実装してみます。

開発環境: Laravel 5.8、Vue 2.6、Googhe Chrome 75、nginx + php-fpm

前提として

Laravelにphp artisan make:authで基本のログイン認証機能が使えるようになっていて、さらにテストユーザーが登録されていることが前提です。

もしまだ準備していない方は、以下ページの「ログイン、パスワードリマインダー機能を構築」を参考にして事前に作業をしておいてください。

【Laravel5.6】インストール直後にやること3点

また、WebAuthnはローカルであってもHTTPS接続が必須です。もしnginxを使っているようでしたら以下を参考にしてみてください。

コピペでOK!ローカル環境にHTTPSを導入する(nginx編)

準備

実際に開発を始める前に必要になる準備をしておきましょう。
WebAuthnに必要なパッケージをインストールするので、まずはそのパッケージに必要なPHPの拡張機能がインストールしておきましょう。

sudo apt install php7.2-gmp

インストールが完了したらウェブサーバーを再起動しておいてください。(httpdの方はsudo systemctl restart httpd

sudo systemctl restart php7.2-fpm

そして、phpinfo()で確認して以下のようになっていればうまくいっています。

では、WebAuthnのパッケージを以下のコマンドでインストールします。

composer require asbiin/laravel-webauthn

パッケージをインストールしたら必要なファイルをパブリッシュ(コピー)しましょう。

php artisan laravelwebauthn:publish

すると、マイグレーションなどがLaravel側にコピーされますので、以下のコマンドでDBにテーブルを作成しましょう。

php artisan migrate

※ もしforeign keyでエラーが出るようでしたら、連携するフィールドの型がちがっていることが原因ですので、database/migrations/2019_03_29_163611_add_webauthn.phpというファイルを開いて以下のように変更してみてください。

$table->unsignedInteger('user_id');

↓↓↓変更

$table->unsignedBigInteger('user_id');

Laravel側の作業

ではここからが、Laravel内での作業になります。
・・・と言っても実は先ほどインストールしたパッケージがほとんど全てのことをやってくれるので、ほんの少しだけで二段階認証を実装することができます。

ミドルウェアを登録する

まず、パッケージが用意してくれているミドルウェアをLaravelに登録します。
app/Http/Kernel.phpを開いて以下のように専用ミドルウェアを登録してください。

    protected $routeMiddleware = [

        // 省略

        'webauthn' => \LaravelWebauthn\Http\Middleware\WebauthnMiddleware::class,
    ];

登録したミドルウェアをルートに適用する

あとは、先ほどのミドルウェアを2段階認証したいルートに適用するだけです。以下は/homeに適用させている例です。

Route::middleware(['auth', 'webauthn'])->group(function () {

    Route::get('/home', 'HomeController@index')->name('home');

});

テストしてみる

では実際にテストしてみましょう!
まずはセキュリティキーをパソコンのUSBに接続しておきます。

※ ちなみにGoogle Chromeでしたらchrome://usb-internals/にアクセスし、deviceタブをクリックした時に以下のように表示されていたらうまく認識されています。

では、https://example.test/loginにアクセスしてログインしてください。

そして、ログインが完了したらhttps://example.test/webauthn/registerにアクセスします。(このページでセキュリティキーを登録します)

すると上のようなポップアップが表示されてセキュリティキーが光ります(このキーだけの可能性あり)ので、指をタッチ。

もし登録がうまくいくと、リダイレクトします。

では、一旦ログアウトして再度ログインしてみましょう。
ここからが2段階認証になります。

この状態でログインボタンをクリックすると、再度以下のポップアップが表示されますので、同じく指をタッチしてログインしてください。

これで認証ができたら2段階認証は完成です。
お疲れ様でした!

ちなみに

今回利用したパッケージは日本語対応していませんが、以下のようにすることで英語表記を上書きすることができます。

まず、/resources/langフォルダ内にwebauthnというフォルダをつくり、さらにその中にjaフォルダ、さらにその中にerrors.phpmessages.phpというファイルを作成します。

また、翻訳内容はそれぞれ以下になります。

(error.php)

<?php

return [
    'user_unauthenticated' => 'Webauthnで認証する前にログインする必要があります。',
    'auth_data_not_found' => '認証データが見つかりません。',
    'create_data_not_found' => '登録データが見つかりません。',
    'object_not_found' => 'オブジェクトが見つかりません。',

    'not_supported' => 'ブラウザがWebAuthに対応していません。',
    'not_secured' => 'WebAuthnではセキュアな接続が必要になります。もしHTTP接続でテストする場合は「localhost」を使ってください。',
    'key_already_used' => 'このキーはすでに登録されています。もう一度登録する必要はありません。',
    'key_not_allowed' => '処理がタイムアウトしたか拒否されました。',

];

(messages.php)

<?php

return [
    'buttonAdvise' => 'セキュリティキーにボタンがあるようでしたら、押してください。',
    'noButtonAdvise' => 'もしないようでしたら、一度外してもう一度接続してください。',
    'success' => 'キーが検出され、認証されました。',
    'insertKey' => 'セキュリティキーを接続してください。',
    'cancel' => 'キャンセル',

    'auth' => [
        'title' => 'セキュリティキー認証',
    ],

    'register' => [
        'title' => 'セキュリティキーを登録',
    ],
];

これらを設置すると以下のようになります。

おわりに

ということで今回はWebAuthnLaravelで実装してみました。

実はですが、当初は認証からキーの登録まで全て自分でコードを書いていたのですが、正直なところ(おそらくセキュリティには必要なのだろうとは思うのですが)「なんでこんな仕様なの!?」みたいなデータ構造になっていたので、結果的に公開されているパッケージを使うことにしました。

まだ今のところ、それほどPHPWebAuthnのパッケージは多いわけではないようですが、これからWebAuthnが人気になってきたら、きっとLaravel本家の方でも実装していくんじゃないかとも思いますので、これからも情報チェックをしていきたいと思います。

ぜひみなさんも強力な認証システムを構築してみてはいかがでしょうか。

ではでは〜!