今すぐできる、Webサイトへの2要素認証導入

(Last Updated On: 2018年8月14日)

Google、Facebook、Amazon(AWS)、Githubなど、大手Webサービス会社が2要素認証を取り入れてしばらく経っています。自分のWebサイトでも2要素認証を導入したい!と思ったことは無いですか?

簡単に可能です!

パスワード認証だけではもう安全とは言えません。ぜひ2要素認証を自分のサービス/プロダクトに導入してください。

 

2要素認証とは?

2要素認証(2 Factor Authentication)とはパスワードとは別の認証コードを利用してユーザーを認証する方式です。2段階認証(2 Step Authentication)と呼ばれることもあります。複数の認証要素を利用して認証するので多要素認証(Multi Factor Authentication) とも呼ばれています。AWSでは2要素認証をMFAと呼んでいます。

2要素認証に利用する一時的なパスワード(OTP:One Time Password)の生成方法は2つあります。(他に色々ありますが省略)

  • 時間ベースのOTP(TOTP:Time-based One Time Password RFC 6238)
  • カウンタベースのOTP(HOTP:HMAC-based One-Time Password RFC 4226

Googleの2要素認証はTOTPを利用しています。詳しい解説やアルゴリズムはRFCを参照してください。TOTPのRFCにはJavaコードによるサンプルも記載されています。

2要素認証の原理は簡単です。秘密のシード(種、キー、シークレット)となる情報をサーバー側とクライアント側で共有し、時間、順序などに基き一時的または使い捨てパスワードを生成します。攻撃者はパスワード以外に、2要素認証設定時にしかやり取りされない秘密のシード情報を知らない限り、正しい使い捨てパスワードを送信することはできません。

TOTPは名前の通りシードと時間をベースにパスワードを生成します。時間と共にパスワードは変化します。HOTPはシードとハッシュ関数を使って一時的パスワードを生成します。利用した回数によってパスワードが変化します。HOTPを利用すると同期がズレることがあります。ズレた場合、連続する2のパスワードを送信して再同期を行うなどの処理が必要です。多少、利用面で不便なのでTOTPを採用しているケースが多いです。

Google認証システム(Google Authenticator)はこのRFC 6238に基づく使い捨ての数字6桁の認証コードを生成します。Google認証はAndroid, iPhone, Blackberryをサポートしているので使い勝手も良いです。RFCで規格化されているのでGoogle認証のみでなくRFC 6238のTOTPをサポートするアプリ/デバイスならどれでも使えます。

利用するサイトはどのWebサイトでも構いません。もちろん自分のWebサイトにも使えます。Webアプリケーション以外での利用も可能です。

 

2要素認証の利用

ここではPHPでGoogle認証システムを自分のサイトに導入する方法をできる限り簡単に紹介します。Google認証のライブラリにはGoogleAuthenticatorを利用します。

composerを使ってインストールしても良いのですが、exampleコードもダウンロードしたいのでgitでクローンします。

$ cd <PHPが有効なWebサーバーのドキュメントルート>
$ git clone  https://github.com/PHPGangsta/GoogleAuthenticator.git

GoogleAuthenticatorディレクトリが作成されます。※

※ 注意:インターネットに接続している環境ではライブラリやテストコードを外部からアクセス可能な場所に置いてはいけません。

サンプルプログラムの実行

WebブラウザでGoogleAuthenticator/example/example1.phpにアクセスします。次のような出力が得られます。

Secret is: 2ZDZXLT7CJ3O5M3L

Google Charts URL for the QR-Code: https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2FBlog%3Fsecret%3D2ZDZXLT7CJ3O5M3L

Checking Code '938186' and Secret '2ZDZXLT7CJ3O5M3L':
OK

example1.phpではシード(シークレット)となる値に”2ZDZXLT7CJ3O5M3L”(この値は秘密にしなければならない)を設定し、Google認証に読み込む為のQRコード用URLを生成しています。QRコード画像生成にはGoogle Chartsを利用しています。

https://chart.googleapis.com/chart?chs=200×200&chld=M|0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2FBlog%3Fsecret%3D2ZDZXLT7CJ3O5M3L

URLをクリックするとGoogle認証などで読み込めるQRコードが表示されます。通常はimgタグでHTMLページに埋め込んで使います。

TOTP Setting QR code

Google認証などでQRコードを読み込むと新しいOTPが追加されます。QRコードを利用しなくてもシード(シークレット/キー)を手動で入力して設定することも可能です。

QRコード生成にGoogle Chartsを使うと秘密にすべきシードが送信されてしまいます。これが困る場合、別のプログラムなどでQRコード画像を生成しても構いません。以下の例ではLinux上でQRコードを生成するqrencodeと画像を表示するeogと使ってTOTP設定用のQRコードを表示しています。

$ qrencode -o tmp.png "otpauth://totp/Blog?secret=2ZDZXLT7CJ3O5M3L"
$ eog tmp.png 

Screenshot from 2015-04-18 13:33:47

最後に2要素認証のパスワードとシークレットを使い、その結果が正しいかチェックしています。”OK”と表示されているので正常に動作しています。

otpauth URIのフォーマット

Google認証の設定用QRコードに埋め込む文字列にはotpauth URIを利用します。 認証デバイスに読み取らせるQRコードの中身はotpauth URIのみが入っています。otpauth URIフォーマットはとても簡単です。

otpauth://totp/Blog?secret=2ZDZXLT7CJ3O5M3L
  • otpauth://totp/  – TOTPであることを指定
  • Blog – Google認証などで「ユーザー名」とし「Blog」を表示
  • secret=2ZDZXLT7CJ3O5M3L -シード(シークレット)として「2ZDZXLT7CJ3O5M3L」を設定
  • 発行元を追加したい場合は「issuer=名前」を追加

例えば、ユーザー名を「yohgaki@php.net」、発行者を「PHP Project」とする場合

otpauth://totp/yohgaki@php.net?secret=2ZDZXLT7CJ3O5M3L&issuer=PHP Project

などとします。詳しくはこちらを参照してください。

サンプルプログラムのコード

以下はexample1.phpのコードです。

<?php

require_once '../PHPGangsta/GoogleAuthenticator.php';

$ga = new PHPGangsta_GoogleAuthenticator();

$secret = $ga->createSecret();
echo "Secret is: ".$secret."\n\n";

$qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret);
echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";


$oneCode = $ga->getCode($secret);
echo "Checking Code '$oneCode' and Secret '$secret':\n";

$checkResult = $ga->verifyCode($secret, $oneCode, 2);    // 2 = 2*30sec clock tolerance
if ($checkResult) {
    echo 'OK';
} else {
    echo 'FAILED';
}

このコードから解るように、TOTPの利用は非常に簡単です。サンプルのみでなくTOTPを実装するクラスも7KB弱ととても簡単なコードになっています。

 

2要素認証導入の注意点

このように2要素認証の導入は簡単ですが、注意すべきこともあります。

  • OTPを生成するデバイスを無くしたり、アプリ/アプリデータを削除した場合にログインできなくなる

当たり前のことですが、OTPを生成できなくなるとログインできなくなります。このため、何らかのリカバリー方法を提供することを忘れないでください。利便性を考えるとメールに記載したURLよる設定変更(2要素認証無効化)が良いですが、セキュリティを考えるとGoogleなどが行っているようにリカバリー用のコードを予め保存してもらう方が良いでしょう。

このブログにもTOTPを導入していますが、リカバリー方法がなかった為、携帯電話の修理交換でデータベースの値を直接操作して回復させました。(今のGoogle Authenticator for WordPressはリカバリコードがあります)一般ユーザーにこのような操作は不可能でしょう。リカバリー方法は必ず作るようにしてください。

折角TOTPを導入してもシード(シークレット)が漏れては意味がありません。

  • 認証デバイス設定用のQRコード画像はHTTPSを利用する
  • QRコード画像がキャッシュされないように注意する

HTTPSが利用できない場合、安全なネットワークから2要素認証の設定をしてください、と注意書きをすると良いでしょう。

セッションハイジャックなどでセッションを乗っ取られた場合にOTPのシード(シークレット)が盗まれては困ります。

  • 使用済み認証デバイス設定用のQRコードは二度と表示しない
  • シード(シークレット)は文字列としても表示しない

TOTPは時間ベースです。OTP生成デバイスとサーバーの時刻がズレないようにしなければなりません。

  • NTPなどを使ってサーバーの時刻を正確に保つ

OTPデバイスの設定ミスなどで正しく動作していないかも知れません。無効な状態でOTPを有効にするとログインできなくなります。

  • OTPが正しく設定されたか確認(OTPデバイスが生成した一時的パスワードを入力)してから有効化する

一度使ったTOTPはサーバー側で無効にすると安全性が高まります。

  • TOTPは一度だけ使えるようにする(再生攻撃防止)
  • 既に使った無効なOTPの利用は警告する(MITMなどで攻撃者が先に使った可能性がある)

TOTPの使い捨てパスワードは一定期間有効です。同じ情報で認証できるので一度使ったら無効化しなければなりません。これの実装にはmemcachedなど、自動的にデータの有効期限は管理して破棄してくれるシステムが便利です。memcachedが利用できない場合も、データベースに保存し、定期的に古い情報を削除するのも難しくはありません。

  • OTPは「ユーザー名とパスワード」と一緒に表示/処理する。(パスワード解析難易度の向上)

OTPを別の画面で表示/処理する実装もよく見かけます。しかし、この実装ではパスワードの総当たり攻撃を容易にしてしまいます。OTPで保護されているとはいえ、パスワードの解析を容易にする実装は安全性に劣る実装です。ユーザー名、パスワード、OTPは「一緒に処理」しなければなりません。

ユーザー名が確定しないと2要素認証の設定が完了しているか分らないことがOTPを別途に処理する理由だと思います。OTP入力フィールドも表示し、未設定の場合は空のまま送信してもらう方が良いです。ユーザーが混乱するの避けたいなら、ブラウザにOTP設定済みフラグクッキーを保存し、「最初だけはユーザー名とパスワードのみのフォームで処理」すると良いでしょう。

OTP未設定の場合

  1. ユーザーがOTP設定済みかクッキーでチェックする
  2. ユーザー名とパスワードの入力のみ表示する
  3. OTP未設定なのでユーザー名とパスワードのみで認証する

OTP設定済みの場合(OTP設定済みでクッキーなし)

  1. ユーザーがOTP設定済みかクッキーでチェックする
  2. ユーザー名とパスワードの入力のみ表示する
  3. ブラウザにOTP設定済みクッキーを設定し、再度認証する
  4. ユーザー名、パスワード、OTPが入力できるフォームを表示し認証する

OTP設定済みの場合(OTP設定済みでクッキーあり)

  1. ユーザーがOTP設定済みかクッキーでチェックする
  2. ユーザー名、パスワード、OTPが入力できるフォームを表示し認証する
  3. (もしユーザーがOTPを無効にしていた場合、OTP設定済みクッキーを削除し「OTP未設定の場合」の処理を行う)

上記のような仕様でも構わないですが、2要素認証の利用をお勧めするため、常にログインフォームにOTP用フィールドを表示する方が良いです。OTP用フィールドのヘルプとして、2要素認証のメリット/設定方法を記載、2要素認証を設定していなければ空で良い、ことを説明すると良いでしょう。

 

2要素認証導入の流れ

今までの説明で自分のサイトに2要素認証をどのように導入すれば良いかわかると思います。通常の場合、以下のような流れになるでしょう。

  1. GoogleAuthenticatorなどの2要素認証用のライブラリを配置する
  2. ユーザーデータベースに「2要素認証のシード(シークレット)」用の文字列カラム(NULLの場合は無効)、最後に使ったOTP保存用のカラムを追加する
  3. ユーザー設定画面などに「2要素認証設定」を追加する。2要素認証を有効化した場合、ユーザーデータベースにシード(シークレット)を保存する。
  4. 2要素認証が有効な場合、ログイン画面にOTPを入力するフィールドを追加する。

OTP生成デバイスが使えなくなった場合のリカバリー方法はサイトのセキュリティポリシーに合わせて適切な方法を選択して実装してください。

  • リカバリーコードを利用する(お勧め)
  • リカバリーURLをメールで送信する
  • ユーザーによるリカバリーは認めない(管理者がリセットする、など)

などの選択肢があります。

 

他の言語の2要素認証ライブラリ

JavaのサンプルコードはRFCに載っています。少し探すだけで既に実装済みのライブラリが見つかると思います。

Railsならdeviseだと思います。他に良い物があったらコメントをお願いします。

Pythonには以下のライブラリがあるようです。

Node.jsには以下のライブラリがあります。

Google認証がリリースされた頃(約4年前)に作られた物が多いです。色々あるので調べてから選択すると良いと思います。使う前にタイミング攻撃に脆弱なコードでないか、確認してから使うと良いです。

 

WordPressの2要素認証プラグイン

WordPressにはTOPTの2要素認証プラグインがあります。これは本当に直ぐに導入できるので、Wordpressユーザーにお勧めです。

シークレットがユーザー情報のページに表示されてしまう問題を作者に連絡したところ、リカバリーコードと同様にパスワードを入力してから表示する方式に修正するとのことでした。

このブログにもパスワードの辞書攻撃、ブルートフォース攻撃らしきアクセスが大量にあります。私のパスワードは簡単に分かるようなパスワードではないですが、このプラグインを入れておけばさらに安心です。

参考:今すぐできる、WordPressサイトへの2要素認証導入

 

まとめ

「パスワードが危い」と言われて久しいです。2要素認証を導入すれば安全性がかなり向上します。

  • 2要素認証導入はとても簡単!
  • シード(シークレット)は秘密にする(HTTPSを使う、二度と表示しない)
  • サーバーの時刻は正確に(NTPを使う)
  • リカバリー方法を作る

多少の作業は必要ですが、今日からでも自分のWebサイトに2要素認証を導入可能なくらい簡単です!

参考:

 

投稿者: yohgaki