HTTP クライアントを作ってみよう(5) - Basic 認証編 -

前へ << HTTP クライアントを作ってみよう(4) HTTP クライアントを作ってみよう(6) - Digest 認証編 - >> 次へ

web における認証

まずはサンプルを見てください。
認証の例: http://X68000.q-e-d.net/~68user/net/sample/http-auth/secret.html
(ユーザ名: hoge、パスワード: fuga で閲覧できます)
上記 URL をクリックすると、以下のような認証画面が表示されます (IE6 のダイアログと、Mozilla のダイアログ)。



ここで正しいユーザ名・パスワードを入力するとファイルを閲覧できます。 間違ったユーザ名・パスワードを入力すると、上記の認証画面が何度でも表示されます。 もしキャンセルボタンを押すと、以下のような画面が表示されます。

Authorization Required

telnet で確認

まずは telnet で http://X68000.q-e-d.net/~68user/net/sample/http-auth/secret.html にアクセスして、どのような仕組みになっているのかを確認しましょう。
% telnet X68000.q-e-d.net 80
GET /~68user/net/sample/http-auth/secret.html HTTP/1.0 (リターン) 
Host: X68000.q-e-d.net:80 (リターン)
(続けてリターン)
HTTP/1.1 401 Authorization Required
Date: Fri, 03 Dec 2004 15:43:35 GMT
Server: Apache/2.0.52 (FreeBSD)
WWW-Authenticate: Basic realm="Secret File"
Content-Length: 491
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Authorization Required</title>
</head><body>
<h1>Authorization Required</h1>
<p>This server could not verify that you
are authorized to access the document requested.
(略)
以下の 3点に注目してください。
  • サーバが返すレスポンスコードが 401 となっている。
  • サーバが WWW-Authenticate: Basic realm="Secret File" というヘッダを返している。
  • ボディ部の HTML は、認証画面でキャンセルボタンを押したときの表示と同じ。
ブラウザはレスポンスコード 401 が返ってくると「認証が必要なページである」と判断し、 ユーザ名・パスワード入力のためのウィンドウを表示するわけです。

認証方法にはいくつかの種類があります。例えば Basic 認証と Digest 認証があります (と言っても、それ以外の認証方法を当ページ管理人は知りませんが)。 この例では

WWW-Authenticate: Basic realm="Secret File"
というヘッダが示すとおり、サーバ側は Basic 認証を要求しています。
「Basic 認証」は、「基本認証」「ベーシック認証」と言うこともあります。 なぜ Basic 認証という名前になったかというと、おそらくは最もシンプルで基本的な認証方法だったからでしょう。
Basic 認証において、ユーザ名とパスワードを送信する方法を説明します。

まずユーザ名とパスワードをコロンで結合します。 もしユーザ名「hoge」、パスワード「fuga」の場合、「hoge:fuga」という文字列を作るわけです。 それを BASE64 でエンコードします。 「hoge:fuga」を BASE64 エンコードすると「aG9nZTpmdWdh」となります。

UNIX のコマンドでは、以下のようにして確認できます。
% echo -n 'hoge:fuga' | base64 -e
% echo -n 'hoge:fuga' | openssl enc -e -base64
% echo -n 'hoge:fuga' | nkf -MB
% echo -n 'hoge:fuga' | perl -MMIME::Base64 -ne 'print encode_base64($_)'
参考:「UNIX の部屋」 base64opensslnkf

上記の Perl の例では MIME::Base64 モジュールを使っていますが、 Perl-5.8 以降であれば標準でインストールされています。 それ以前の Perl であれば適当にインストールしてください。

さきほどのコネクションは既にサーバ側から切断されていますので、 再度 web サーバに接続します。 このとき、

Authorization: Basic (BASE64 エンコードしたユーザ名とパスワード)
というヘッダを送ります。

% telnet X68000.q-e-d.net 80
GET /~68user/net/sample/http-auth/secret.html HTTP/1.0 (リターン) 
Host: X68000.q-e-d.net:80 (リターン)
Authorization: Basic aG9nZTpmdWdh
(続けてリターン)
HTTP/1.1 200 OK
Date: Fri, 03 Dec 2004 16:42:56 GMT
Server: Apache/2.0.52 (FreeBSD)
Content-Length: 176
Content-Type: text/html; charset=euc-jp

<html>
<head>
  <META HTTP-EQUIV="Content-Type" Content="text/html; charset=EUC-JP">
  <title>ひみつのファイル</title>
</head>

<body>
これは秘密ファイルです。
</body>
</html>
認証で保護されているファイルを見事に取得できました。

では、誤ったユーザ名・パスワードを送るとどうなるか試しましょう。

% telnet X68000.q-e-d.net 80
GET /~68user/net/sample/http-auth/secret.html HTTP/1.0 (リターン) 
Host: X68000.q-e-d.net:80 (リターン)
Authorization: Basic aaaaa ●誤ったユーザ名・パスワードを送信
(続けてリターン)
HTTP/1.1 401 Authorization Required
WWW-Authenticate: Basic realm="Secret File"
(略)

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Authorization Required</title>
</head><body>
<h1>Authorization Required</h1>
<p>This server could not verify that you
are authorized to access the document requested.
(略)
興味深いことに、Authorization ヘッダを付けないときと全く同じレスポンスが返ってきました。

認証は、ログイン・ログアウトではない

適当なページを探して、一番最初のリクエストで Authorization ヘッダを送信してみてください。 つまり、サーバにレスポンスコード 401 を返させることなく、 初めから Authorization ヘッダを付けてリクエストしてください。 ユーザ名・パスワードが正しければ、ちゃんとページを取得できるはずです。

また、一度 Authorization ヘッダを付けて認証を通った後に、 Authorization ヘッダなしでリクエストを送ってみてください。 やっぱりレスポンスコード 401 が返ってきてしまいます。

つまり、web サーバが行っていることは、

  • 認証が必要なページに Authorization ヘッダなしでリクエストしてきた場合、401 を返す
  • 認証が必要なページに Authorization ヘッダ付きでリクエストしてきた場合、 (ユーザ名・パスワードが正しければ) ページ内容を表示する
これだけです。

認証が必要なページを取得する場合は、リクエストごとに毎回 Authorization ヘッダを送信する必要があります。HTTP の認証には、ログイン・ログアウトというような概念がないのです。 これはしっかりと認識しておいてください。

ログイン・ログアウトの概念はありませんが、以下のような CGI プログラムを使うことで、 疑似的にログアウトすることもできます (訪問者に、認証画面でキャンセルボタンを押してもらう必要があるので、 かなり無理がありますが)。
#!/usr/local/bin/perl

print <<END;
Content-type: text/html
Status: 401 Authorization Required
WWW-Authenticate: Basic realm="Secret File"

<html><head><title>401 Authorization Required</title></head>
<body><h1>Authorization Required (by CGI)</h1></body></html>
END

Basic 認証 (基本認証) の利点と欠点

Basic 認証はシンプルな認証方法という利点があります (そのおかげで、 早い時期からほぼ全ての web サーバ・ブラウザに実装されていました)。 しかし「盗聴に弱い」という大きな弱点があります。 tcpdump や ethereal などでパケットをダンプしてしまえば、
Authorization: Basic aG9nZTpmdWdh
というヘッダは簡単に得られます。これを Base64 デコードすれば「hoge:fuga」が手に入ります。 セキュリティもへったくれもないですね。
Base64 エンコードは暗号化ではありません。ただのコード変換です。 鍵のない変換を暗号化とは呼びません。

ファイル設定

今回のサンプルで使用した認証関連の設定ファイルは以下の通りです。 Apache の初期設定では、「.ht」から始まるファイルを閲覧できないようになっています
Apache の設定ファイル httpd.conf に以下のように記述されているはずです。
<Files ~ "^\.ht">
  Order allow,deny
  Deny from all
</Files>
このサンプルでは、この制限を外すよう .htaccess にて設定していますが、 普通はこういうことはしません。また、パスワードファイルを public_html 以下に置くのもお勧めできません。
  • .htaccess
    apache 設定ファイル。.htaccess と .htpasswd を公開するため、allow from all としている。
  • .htpasswd
    パスワードファイル。以下のようにして作成したもの (ユーザ名: hoge、パスワード: fuga)。
    % htpasswd -c .htpasswd hoge
    New password: (fuga と入力)
    Re-type new password: (fuga と入力)
    Adding password for user hoge
    
  • secret.html
    秘密のファイル (ユーザ名: hoge、パスワード: fuga)
前へ << HTTP クライアントを作ってみよう(4) HTTP クライアントを作ってみよう(6) - Digest 認証編 - >> 次へ

ご意見・ご指摘は Twitter: @68user までお願いします。