Home > Ticket | English

Ticket

Contact

HTTP認証による短時間パスワードの試験実装

横浜工文社はデータベースホスティングサービスを構築中です(近々公開予定)。 その過程でアカウント登録のため「ワンタイムパスワード(one-time password)」のようなものが必要になりました。 そしてふたつの「短時間パスワード(short-time password)」の試験実装を行いました。 SQLテーブルを用いたものと、アパッチのHTTP基本認証(HTTP BASIC Authentication)機構を用いたもののふたつです。 結局は前者のテーブル利用型を採用しました。 しかし後者のHTTP認証を利用する方式は簡単かつ有用であることがわかりました。 そこでHTTP基本認証を用いた短時間パスワードを実装したコード(短いBashシェルスクリプト)を公開します。

ソースコード: ticket.sh

この短時間パスワード実装は「チケット」と呼んでいます。 保護されたWebページやWebアプリへのアクセスを一定時間だけ可能とする許可証という意味です。

当初このチケット実装を「ワンタイムパスワード」と呼んでましたが正確には「短時間」です。パスワード入手者は一定時間内であれば保護ページに複数回アクセスできます。それで呼び方を変えました。

チケットの動作概要

チケットの動作概要です。

Mailer Request -> Handler Mailer <- Ticket Reply Click Browser URL -> Web Server Browser <- Page Confirm Inline SVG not supported by your browser

これは筆者が「事前確認」とよぶ「メールボックス確認(mailbox confirmation)」の一種です。

もうひとつ「事後確認」と呼ぶものがあります。 ショッピングサイトなど新しいWebサービスに登録する際、まずメールアドレスや住所など個人情報をタイプします。 登録作業の最後にサービスサイトが送信する電子メールをチェックするよう促されます。 メールで送られたURLをクリックすると再びショッピングサイトに誘導されます。 サイト側はここで訪問者が電子メールアドレスの正当な所有者であることが確認できます。

Visit site Register Check email Click URL Confirmed Inline SVG not supported by your browser

このページで説明するチケットサービスはショッピングサイトの例とは異なります。 メールボックス確認をサービスにアクセスする前に行うからです。 このためチケットスタイルを事前確認、ショッピングサイトのスタイルを事後確認と呼んでいます。

筆者は同じ事前確認型のふたつのメールボックス確認方式を試しました。

最終的に筆者はテーブル利用のチケットに決めました。 ユーザ名やパスワード以外の訪問者情報も保持したかったからです。 限定利用のパスワードを保存する方法としてはデータベーステーブルを用いる方式がより一般的と思います。 しかし基本認証を用いた短時間パスワードは一定のシナリオでは有効であると思います。 利点は次のとおりです

以下、HTTP基本認証で短時間パスワードを実装したコードの主要部分と実行例を示します。

下記例ではダミーのドメイン名(訪問者はexample.com、サーバはexample.net)を使います。実際には当方のドメイン名でテストを実施。

メールの送受信

最初に訪問者は希望のサービスや見たいページを提供するドメイン宛てに電子メールを送ります。 標題(Subject)にはアクセスしたいURLパス(ドメイン名を除く)を指定します。 メールの本文は何でもよく、空でかまいません。

From: guest@example.com
To: ticket@example.net
Subject: /admin/auth_account.mdm

(No body text)

受け側のドメインのメールサーバが返すメールは次のとおりです。

Hi, guest@example.com,

You have requested a ticket to visit /admin/auth_account.mdm on our site.
Click the following URL and type the username and password on a popup dialog:

  https://example.net/admin/auth_account.mdm
    Username: guestHexampleAcom
    Password: ee6cac9a

For shortcut, you can just click the following URL: 

  https://geustHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm

If you are not expecting this email, please just ignore and discard it.

Thank you.
Ticket Office

この時点で、受け側ドメインで動作するアパッチWebサーバにはユーザ名とパスワードが登録されており、訪問者が対象ページにアクセスすれば認証が成功する状態となっています。

auth-dialog

メールハンドラの設定

指定の電子メールアドレスに届くメッセージを受信し何らかのメッセージを自動的に返信するには、サーバ側でメールハンドラスクリプト(あるいはプログラム)を設定しておく必要があります。 筆者が使用中のメールサーバのPostfixの場合、次の行を/etc/aliasesに追加します。

[/etc/aliases]

ticket: "|/usr/local/maliases/ticket.sh"

この設定により'ticket@example.net'にメールが届くと'ticket.sh'スクリプトが実行されます。 メールの中身が標準入力を経由してスクリプトに渡されます。

実際には筆者は/etc/aliasesとは別のファイルを使用し、そのファイルを/etc/main.cfの'alias_maps'行に追加しました。そのファイルの所有者の権限でスクリプトを実行するためです。 さらに、デバッグ・ロギング目的でstdoutおよびstderrをファイルにリダイレクトするようにしました。
→ ticket: "|/usr/local/maliases/ticket.sh >> /var/log/maliases/ticket.log 2>&1"

メール文の処理

受信したメールメッセージはメールハンドラの標準入力からmbox形式(From行にメッセージが続く)で渡されます。

From guest@example.com  Thu Jul 09 12:55:09 2020
Return-Path: <guest@example.com>
X-Original-To: ticket@example.net
Delivered-To: ticket@example.net
Received: ...
To: Live Embedder Ticket <ticket@example.net>
From: "Guest" <guest@example.com>
Subject: /admin/auth_account.mdm
Message-ID: <2e5e2435-0c74-fd8b-723f-583ed02674be@...>
Date: Thu, 9 Jul 2020 12:55:07 +0900
User-Agent: ...
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Content-Language: en-US
(one blank line)

送信者のメールアドレスはSENDER環境変数でも渡ってきます。 メールから取り出したい情報は訪問者が訪れたいURLパスを含む標題行(Subject)だけで、それは下記のようにawkを用いて取得しました。

# get path info from Subject header
path_info=$(cat | awk '/^Subject:/{print $2;}');
echo "path_info: $path_info"
                 |
                 v
path_info: /admin/auth_account.mdm

ユーザ名とパスワードの生成

実際のところチケットとして使う一時利用のユーザ名とパスワードはなんでもかまいません。 攻撃者が当該Webサーバのパスワードファイルに偽造したユーザ名とパスワードを書き込めないかぎり偽のチケットを作ることはできません。 筆者の実験では送信者のメールアドレスと/dev/urandomから取得した4バイトの乱数の16進表記を用いました。 URLへ埋め込めるようメールアドレスに含まれる記号を英大文字に変換しました。

# sanitize email address so that it can be embedded in URL
username=$(echo $SENDER | tr "[:upper:]" "[:lower:]" | tr "@_.-" "HUAD")
echo "username: $username"
                |
                v
username: guest2HexampleAcom

# get a password from random number generator
password=$(xxd -p -l 4 /dev/urandom)
echo "password: $password"
                |
                v
password: ee6cac9a

のちに、perlモジュールを使って上記の四つ以外の記号も変換するようにしましたが、わかりやすさを重視して、当初の短いシェルコードのまま掲載します。

パスワードファイルにユーザ名とパスワードを設定

アパッチのhtpasswdというコマンドでプレーンテキストのパスワードファイルが作成できます。 HTTP基本認証でこのファイルのパスワードが使われます。

基本認証で保護された領域にWebクライアントがアクセスすると、アパッチはユーザ名とパスワードを求め、管理下の認証データと比較します。 htpasswdが作成するパスワードファイルはそのようなデータのひとつです。

# register username and password pair to Apache password file
[ -e $HTPWFILE ] || option_c='-c'
htpasswd $option_c -b $HTPWFILE $username $password
if [ $? -ne 0 ]; then
  >&2 echo "Failed to write to $HTPWFILE: $username";
  exit 0
fi

HTPWFILEはアパッチが該当のURLにアクセスがあった場合にチェックするパスワードファイルのパスを含むシェル変数です。 '-c'オプションでhtpasswdコマンドにパスワードファイルがすでに存在するかどうかを伝えます。

上記スクリプトはエラーが起こっても終了コードはゼロを返します。 これはメールサーバが送信者にエラーメールを返すのを避けるためです。

コマンドの動作をコマンドラインで試してみます。

$ htpasswd -c -b /var/www/etc/htp_tickets guestHexampleAcom ee6cac9a

$ ls /var/www/etc/htp_tickets
-rw-rw-r-- 1 www-data www-data    53 Jul  9 12:55 htp_tickets

$ cat /var/www/etc/htp_tickets
guestHexampleAcom:$apr1$H8QE2KWc$99cegAYGAGohWy7O3aPMc/

URLの作成

ユーザ名とパスワードは下記のようにURLに埋め込むことができます。

full_url="https://$username:$password@example.net$path_info"
echo "full_url: $full_url"
                |
                v
full_url: https://guestHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm

要求者へ返信

postfixパッケージに含まれるsendmailコマンドを使って送信者にメールを返信します。 SENDMAIL変数にはsendmailコマンドへのパスが設定されています(筆者の環境の場合は/usr/sbin/sendmail)。 RECIPIENT変数はメールハンドラに割り当てられたメールアドレスにPostfixが設定してくれます。

$SENDMAIL -F "Ticket Office" -f "$RECIPIENT" $SENDER <<REPLY_CONTENT
Subject: Re: $path_info

Hi, $SENDER,

You have requested a ticket to visit $path_info on our site.
Click the following URL and type the username and password on a popup dialog:

  $plain_url
    Username: $username
    Password: $password

For shortcut, you can just click the following URL: 

  $full_url

If you are not expecting this email, please just ignore and discard it.

Thank you.
Ticket Office
REPLY_CONTENT

(メール文例は掲載済み)

パスワードエントリの削除

筆者は短時間パスワードの存続時間を1時間と決めました。 この要件を実現するのにatコマンドを使用してハンドラスクリプトの実行から1時間後にパスワードファイル中の該当パスワードエントリを削除するようスケジュールします。

# schedule deletion in one hour
at now + 1 hour <<AT_COMMAND
htpasswd -D $HTPWFILE $username
logger -s "[ticket.sh] ticket expired: path $path_info given to $SENDER"
AT_COMMAND

htpasswdコマンドの-Dオプションは指定のユーザ名を持つエントリを削除します。 loggerコマンドはsyslogサーバにログメッセージを送信するのに使用します。 loggerコマンドの'-s'オプションはそのメッセージを同時に標準エラーへも出力することを指定します。

メールハンドラスクリプトのログ出力

下記はスクリプトのstdinとstdoutをファイルにキャプチャしたものです(ドメイン名は変更)。

[/var/log/maliases/ticket.log]

[ticket.sh] 20/07/09-12:55:09 SENDER = guest@example.com, RECIPIENT = ticket@example.net
path_info: /admin/auth_account.mdm
<13>Jul  9 12:55:09 www-data: [ticket.sh] ticket requested for /admin/auth_account.mdm to guest@example.com
username: guest2HexampleAcom
password: ee6cac9a
plain_url: https://example.net/admin/auth_account.mdm
full_url: https://guestHexampleAcom:ee6cac9a@example.net/admin/auth_account.mdm
Adding password for user guestHexampleAcom <- from htpasswd
warning: commands will be executed using /bin/sh <- from at command
job 19 at Thu Jul  9 13:55:00 2020
<13>Jul  9 12:55:09 www-data: [ticket.sh] ticket issued for /admin/auth_account.mdm to guest@examaple.com

次はsyslogへ出力された監査用ログです。

[/var/log/messages]

Jul  9 12:55:09 ... www-data: [ticket.sh] ticket requested for /admin/auth_a
ccount.mdm to guest@example.com
Jul  9 12:55:09 ... www-data: [ticket.sh] ticket issued for /admin/auth_account.mdm to guest@example.com
Jul  9 13:55:00 ... www-data: [ticket.sh] ticket expired: path /admin/auth_account.mdm given to guest@example.com

メールハンドラスクリプト実行の1時間後に削除スクリプトが実行されていることがわかります。

確認のためパスワードファイルの中身をみてみます。

$ ls -l /var/www/etc/htp_tickets
-rw-rw-r-- 1 www-data www-data 0 Jul  9 13:55

$ cat /var/www/etc/htp_tickets
$

パスワードファイルからパスワードエントリがなくなっていることがわかります。

以上、チケット(限定利用のパスワード)の試験実装の動作について説明しました。

まとめ

アパッチの基本認証機構を用いて単純なメールハンドラひとつで短時間パスワードが実装可能であることがわかりました。 Webページ側でプログラミングが不要なため静的ページであっても短時間パスワードでの保護が可能です。

横浜工文社は問題解決方式の探索、実現可能性調査など研究開発試作を請け負います。 ご相談ください。

Presented by Kobu.Com
Written 2020-Jul-10
Updated 2020-Jul-31 fixed: $RECEIPIENT -> $RECIPIENT

Contact

Contact