Saturday, May 24, 2014

あなたのapacheのBasic認証をgoogle 2段階認証に対応させるには

Googleの2要素認証ですが、数年前のリリース以来、少しずつ普及しているようです。

ただ、googleのサービス=例えばgmailとか=でしか使えないし、あるいは自分のwebサービスに導入するにはgoogle様に何らかのお布施をしなければならないんでしょ...と思っているあなた! それは単なる思い込みや誤解です。

目標

  1. 自分が管理するapache上のコンテンツの一部に認証をかけたいとして、
  2. ユーザー名がfooでパスワードがbarだとする。
  3. 導入が比較的お手軽なBasic認証ですませたい。
  4. ただしgoogle2要素認証も使う。スマホのアプリでワンタイムパスワード(以下OTP)を受け取るやつ。
  5. ブラウザ上に出現するbasic認証のダイアログには、アカウントとしてfoo、パスワードとしてbar+OTPを入力する。例えばOTPが123456であればパスワードとして"bar123456"で認証できるようにする

用意するもの

  • linuxマシン。以下の例ではOracleVirtualBox+Vagrantで構築したCentOS6.5を使用(もっと古いディストリでも大丈夫)
  • スマートフォン。android,iphoneどっちでも。

手順

まず、スマホにGoogle認証アプリをインストールしておく。androidならplayストア、iphoneならitunesで「Google認証」で検索すればすぐ見つかります。 既にインストール済みの人はそれをそのまま使えます。

次に、サーバにhttpdやgoogle-authenticatorなどをインストールします。いずれもyumで可能です。ただしgoogle-authenticatorのパッケージはepelリポジトリにしか無いので注意。あとhttpd-develやgccなんかも必要です。

[vagrant@localhost ~]$ cat /etc/redhat-release 
CentOS release 6.5 (Final)
[vagrant@localhost ~]$ sudo yum install http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm
[vagrant@localhost ~]$ sudo yum install httpd httpd-devel subversion google-authenticator
[vagrant@localhost ~]$ sudo /etc/init.d/httpd start

この時点で普通にapacheが起動できるはずです。ブラウザでアクセスして確認しておきましょう。iptablesとかは適宜設定しておくか、切っておきましょう。

なお、サーバの時計をできるだけ正確に合わせておいてください。google認証はシークレットキーと時刻情報からワンタイムパスワードを割り出すので、 スマホ側とサーバ側とで時計が狂いすぎているとうまく認証できません。

さてここでgoogle-authenticator-apache-moduleの登場です。 コンパイル済みのバイナリもダウンロードできますが、 それは古くてOTPだけの認証しかできない(2要素認証になってない)バージョンなので、 最新ソースをcheckoutして自分でビルドします。 同梱の設定ファイルのコピーも忘れずに。

$ svn checkout http://google-authenticator-apache-module.googlecode.com/svn/trunk/ google-authenticator-apache-module-read-only
$ cd google-authenticator-apache-module-read-only
$ make
$ sudo make install
$ sudo cp googleauth.conf /etc/httpd/conf.d/

$ sudo vi /etc/httpd/conf.d/googleauth.conf で下記のように編集。(Alias /tmp /tmp を追加しただけ)

Loadmodule authn_google_module modules/mod_authn_google.so
Alias /tmp /tmp
<Directory /tmp>
    Options FollowSymLinks Indexes ExecCGI
    AllowOverride All
    Order deny,allow
    Allow from all
    AuthType Basic
    AuthName "My Test"
    AuthBasicProvider "google_authenticator"
    Require valid-user
    GoogleAuthUserPath ga_auth
    GoogleAuthCookieLife 3600
    GoogleAuthEntryWindow 2
</Directory>

次に、ユーザー"foo"用の認証設定ファイルの準備。 最初のほうでインストールしたgoogle-authenticatorコマンドを使います。

[vagrant@localhost ~]$ google-authenticator
https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/vagrant@localhost.localdomain%3Fsecret%3DOVLUR4T2XXXXXXXX
Your new secret key is: OVLUR4T2XXXXXXXX
Your verification code is 995999
Your emergency scratch codes are:
  39831342
  73015310
  75610143
  61758264
  10475486

Do you want me to update your "~/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) y

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y
[vagrant@localhost ~]$ 

何やら楽しそうなURLが表示されました。urlをブラウザにコピペして開いてみましょう。 QRコードが表示されるはずです。 スマホのGoogle認証アプリを起動し、右上のメニューから「アカウントを設定」→「バーコードをスキャン」でブラウザに映っているQRコードを読み取ります。 出力されているsecret keyとverification codeを手作業でスマホに入力する方法でももちろんOKですが、面倒です。

スマホのアプリ画面は次のようになるはずです。赤枠の部分が今回の作業で追加した認証トークンになります。

これでスマホのGoogle認証アプリの準備はできました。サーバ側の設定作業に戻ります。

さきほどのgoogle-authenticatorコマンドの出力結果が ~/.google_authenticator に保存されています。 それを一部コピペ&編集してapacheのユーザー認証設定として使います。

[vagrant@localhost ~]$ cat ~/.google_authenticator 
OVLUR4XXXXXXXXXX
" RATE_LIMIT 3 30
" WINDOW_SIZE 17
" DISALLOW_REUSE
" TOTP_AUTH
39831342
73015310
75610143
61758264
10475486

この内容を少し改変して、/etc/httpd/ga_auth/[ユーザー名] というファイルに格納します。ここではfooというユーザーにしますので、下のような感じ。

[vagrant@localhost ~]$ sudo mkdir /etc/httpd/ga_auth
[vagrant@localhost ~]$ sudo vi /etc/httpd/ga_auth/foo

最終的にこんな結果になるように編集しておき、apacheを再起動したら作業完了です!

[vagrant@localhost ~]$ cat /etc/httpd/ga_auth/foo
OVLUR4XXXXXXXXXX
" RATE_LIMIT 3 30
" WINDOW_SIZE 17
" TOTP_AUTH
" PASSWORD=bar

[vagrant@localhost ~]$ sudo /etc/init.d/httpd restart

PASSWORD=bar というところがポイントです。好きなようにパスワードを決めて書いておいてください。これが2要素認証のうちの第1要素にあたります。

いよいよブラウザでapacheにアクセスしてみましょう。/tmpというURIをファイルシステムの/tmpにAliasし、そこだけ2要素認証がかかるようにしたので、 http://サーバのip/tmp というurlにアクセスします。

いつものBasic認証ダイアログが出現します。ここでユーザー名はfoo、そしてパスワードの欄は bar + ワンタイムパスワードです。 上の画像で言えば、379076を示しているので、ダイアログのパスワードには bar379076 を入力します。/tmp の配下のファイル一覧が見えれば成功です。

補足

本家のwikiに書いてある通り、できればSSLも使うべきです。 そうすればBasic認証のリクエストヘッダも暗号化通信の対象となるため安心です。あと、Basic認証ではなくDigest認証もできるようですね。ためしてないけど。

それから、お気づきの通り、google-authenticatorコマンドはシークレットキーとともにいくつかの緊急用コードも出力してくれます。これはスマホを盗難、紛失、破損などしてOTPを得られなくなってしまった場合に備えての緊急用です。が、google-authenticator-apache-moduleはこれには対応してないようです(詳しく調べてません)。ソースほじろうかと思いましたが疲れたんでこのへんで。

Webの安全のために手をつくしてくれているGoogle先生とプログラマー諸氏に感謝!

No comments:

Post a Comment