今更ですが、Apache のベーシック認証について調べました
この記事は2022/10/01に作成されました。
前書き
一時的にベーシック認証をかけておく、といった作業はよく行いますが、正直、世には
- 秘伝のソース
- 秘伝のブックマーク
- 秘伝の勘
をブレンドして調理するシェフは多いと思います。
僕も毎日がシェフの気まぐれパスタです。
このままではいけないと、少しは Apache サーバーにおけるベーシック認証の仕組みを理解しようと思い、調べものを始めたのがこの記事のタネとなっています。
お急ぎの方は「設定フローチャート判定」へどうぞ。
検証環境
- OS: AlmaLinux release 8.5
- Apache: Apache/2.4.37 (AlmaLinux)
- (PHP): 7.4.19 (cli) (built: May 4 2021 11:06:37) ( NTS )
多分同じ Linux 系統の OS であれば Ubuntu 辺りでも同じように動くと思います。
前提知識
これ以降しばらくは、ベーシック認証についてクライアント視点(≒ブラウザ使用者からの視点)で知っておくべき基礎知識を顔説していますが、設定をお急ぎの方は「設定フローチャート判定」をご覧ください。また時間のある時に読んでみてくださいね。
そもそも、ベーシック認証とは、ユーザー側から見るとこんな感じでユーザー名とパスワードを要求されるアレです。
予めサイトの管理者から共有されたユーザー名とパスワードを入力するとサイトを閲覧することが可能になります。
機能としては以上なのですが、このときブラウザはどのような処理をしているのでしょう?
ベーシック認証の発生条件
まずそもそもですが、 Web ページをアクセスするときは http 通信を用いてサーバーにリクエストを送ります。
このリクエストに対してサーバーは以下のいずれかの反応を示します。
- ステータスコード 200 番台: 正常にコンテンツを提供
- ステータスコード 300 番台: リクエストを受け付けるが、コンテンツは提供しない(例: キャッシュの使用を勧める、リダイレクト先の案内)
- ステータスコード 400 番台: リクエストを受け付けない
- ステータスコード 500 番台: サーバーはサーバー側の問題により、現在リクエストを受け付けられる状況にない
- ステータスコード 100 番台: その他(例: サーバーはまだ処理を継続中、プロトコルの切り替えを要求)
- ステータスコード無し: そもそもサーバーが存在しない、リクエストを受け付けていない
通常、ブラウザ(とユーザー)はステータスコード 200 番台でコンテンツが得られることを期待していますが、ベーシック認証が必要なときはたいていステータスコードが「401 Unauthorized」で返ってきます。
「401 Unauthorized」はクライアントに「有効な認証資格が足りない」という意を示します。
ただし、これ自体は「具体的に必要なのはベーシック認証である」という情報を伝えるものではありません。
ブラウザがベーシック認証のダイアログを表示する決め手になるのはレスポンスにある「WWW-Authenticate」ヘッダーです。
- 「WWW-Authenticate」ヘッダーが存在する
- かつ、その内容が「Basic realm="XXX…"」のような形式
これら条件を満たすとき、ブラウザはユーザーに対してベーシック認証のダイアログを表示することになります。
ちなみにブラウザの開発者ツールを用いてこれを観察しようにも、大抵のブラウザはレスポンスを表示する前にダイアログを出してしまうためリアルタイムの把握が困難です。
(ダイアログでキャンセルを押せば確認可能ですが、個人的には、それがベーシック認証を発生させたレスポンスなのか、認証に失敗したから発生したレスポンスなのか分かり辛かったです)
しかしブラウザ以外のツール、例えば cURL などを用いると最初からレスポンスに「WWW-Authenticate」ヘッダーが存在するのが確認できるでしょう。
例: コマンドライン上の cURL でベーシック認証が必要なページにアクセスしたときのレスポンス
$ curl --include https://noji.dev HTTP/1.1 401 Unauthorized Date: Fri, 30 Sep 2022 19:59:45 GMT Server: Apache/2.4.37 (AlmaLinux) OpenSSL/1.1.1k WWW-Authenticate: Basic realm="Input ID and Password." Content-Length: 381 Content-Type: text/html; charset=iso-8859-1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>401 Unauthorized</title> </head><body> <h1>Unauthorized</h1> <p>This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p> </body></html>
クライアントは認証情報をどのように伝えているのか
ベーシック認証のダイアログに正しいユーザー名とパスワードを入力すると、たいていサーバー側は改めて正常なレスポンスを返してくれます。
この時、ブラウザは再度同じ URL にリクエストを行いますが、ユーザー名とパスワードはどうやってサーバーに伝えているのでしょう?
答えはリクエストに存在する「Authorization」ヘッダーです。
実際にユーザーが入力したユーザー名とパスワードは「:」で連結した後 BASE64 でエンコードされた後、「Basic 」に続けて「Authorization」の値として使用されます。
例:
Authorization: Basic aG9nZTpob2dl
大抵のブラウザは、一度通ったユーザー名とパスワードをブラウザを閉じるまでの間保持してくれますが、実際に開発者ツールを開いて確認してみると、同じドメインに対するリクエストに関してはずっと同じ Authorization ヘッダーが使用されているのが確認できると思います。
これが最も一般的なベーシック認証の解除方法ですが、かつては多くのクライアントソフトで URL に直接ユーザー名、パスワードを指定することもできました。
具体的には「://」とドメインの間に「ユーザー名:パスワード@」の形で指定すると Authorization ヘッダー無しで認証できるという方法です。
例:
https://noji.dev
↓
https://user:password@noji.dev
現在でも cURL などでは動作しますが、ブラウザでは動作したりしなかったりします。
ベーシック認証の設定ファイル
Apache を用いてベーシック認証を設定する場合は「ディレクティブ」と呼ばれる Key & Value 形式の指定を設定ファイルに書き込みます。
まずそもそもとして、この設定を書き込む先のファイルとして二ヶ所候補があります。
・ベーシック認証を掛けたいディレクトリに作成する .htaccess
・Apache が動作する時に読み込まれる設定ファイルである httpd.conf
一般的に知られているかつ、お手軽に設定できるのは前者でしょう。
.htaccess は設定が配置したディレクトリ下に自然と当たる上、設定を再度読み直す必要がありません。
ただし、そもそもの設定として AllowOverride ディレクティブで「None」等が設定されている場合は設定することができません。
その場合は httpd.conf で AllowOverride ディレクティブを「All」などにするか、もしくは httpd.conf で設定をするかを選択することになるでしょう。
しかしそもそも .htaccess が使用できないようなレンタルサーバーでは、当然 httpd.conf 自体も設定不可能な位置にあるハズなので Apache の設定でベーシック認証をかけるのは不可能です。
httpd.conf を扱う場合は .htaccess と比較し以下二点が異なります。
・ベーシック認証をかける対象ディレクトリを <Directory> ディレクティブで指定する
・設定をファイルに書き終えたらデーモンに設定を再読み込みさせる。
.htaccess を用いる場合は設置ディレクトリ以下が自動的に公開範囲となりますが、 httpd.conf はサーバー全体にかかる設定ファイルのため、ベーシック認証をかけるディレクトリを指定するのが良いでしょう。
具体的には以下のようになると思われます。
<Directory /var/www/html/example>
AuthType Basic
AuthName "Input ID and Password."
AuthBasicProvider file
AuthUserFile "/var/www/html/example/.htpasswd"
Require valid-user
</Directory>
また、 httpd.conf はユーザーからアクセスがある度に評価される設定ファイルではなく、Apache を動作させているデーモン(Windows 環境ではサービス)が起動時に評価されます。
従ってデーモンを再起動するか、もしくは設定を再度読み直す必要があるのですが、大抵は ssh を用いて CLI でコマンドを叩くことになるでしょう。
さらに使用しているサーバーによってデーモンを制御するコマンドも異なります。
参考までに、上記検証環境では、
sudo systemctl reload httpd
となります。
これが Alma Linux のような Red Hat 系ではなく Ubuntu のような Debian 系ならデーモンは「httpd」ではなく「apache2」になるので、
sudo systemctl reload apahce2
になることが予想されます。
また、systemctl が使用できないやや古めの Linux ディストリビューションであれば service コマンドを使用することになるため、
sudo service httpd graceful
となるでしょう。
余談:
graceful とはなんぞや、という話を少々。
Apache のデーモンを普通に再起動させると、その瞬間にサーバーへアクセスしている通信が遮断されてしまいます。
そこで通常の再起動ではなく、その瞬間に動いているプロセスは生かし続け、次に発生するプロセスから新しい設定を読み込むのが graceful という指定です。
これを使用すると、今まさに Web サイトへアクセスしているユーザーのアクセスを遮断せずに済むわけですね。
さて、この graceful ですが、かつては service コマンドへ直接指定できました。
しかし、service コマンドの代わりとして標準となりつつある systemctl では graceful を指定することはできません。
(余談の余談ですが、Apache をインストールするとついてくる apachectl コマンドでは今でも graceful を使用できます)
しかし現在の Apache では、systemctl を用いて reload を行った場合、自動的に graceful の挙動になるようです。
なので、上記では
sudo systemctl reload httpd
を用いています。
設定に使用するディレクティブ
世のベーシック認証を解説する記事では以下四つのディレクティブが解説されていることが多いです。
- AuthType
- Require
- AuthName
- AuthUserFile
AuthType については固定で「Basic」と設定します。
Require も固定で「valid-user」を設定すれば問題ないと思われます。
https://httpd.apache.org/docs/2.4/ja/mod/mod_authz_core.html#require
にもある通り、個別に使用できるユーザー名を指定することもできますが、たいていの場合は後述する AuthUserFile で指定するパスワードファイルに列記したほうが良いでしょう。
AuthName はクライアントにどのような認証情報が必要か説明するのに使用される文言情報です。
ただし、最近のブラウザではこの設定をダイアログに表示しないケースも多いです。
ただだからといってこのディレクティブを省略してしまうと上手く動作しないケースもあるので、しっかり設定しておくべきです。
最後に AuthUserFile ですが、これらディレクティブの中では唯一、コピペで済ませることができない設定です。
このディレクティブには、実際にユーザー名とパスワードを設定するファイルの絶対パスを指定します。
これら設定を纏めると、以下のような指定となるでしょう。
AuthType Basic
AuthName "Input your ID and Password."
AuthUserFile /var/www/html/example/.htpasswd
require valid-user
実は、公式の解説を見るとこの四つ以外にも必須とされているディレクティブがあります。
これを用いると後述する .htpasswd のようなファイルではなく、 DBM などのデータベースを用いた認証機構を用いることもできます。
ただし、デフォルトでは値が「file」となっており、ほとんどのサーバーでこれを明示的に変えているところはないのでしょう。
なのでこのディレクティブを省いても実際には問題にならないと考えられるため、世の設定記事にはこのディレクティブが紹介されていないと考えらます。
.htpasswd の作成・設置
ディレクティブとして設定すべきものは前章で説明した通りですが、実際にユーザーに入力してもらうユーザー名・パスワードはどこに用意するのでしょうか?
AuthBasicProvider ディレクティブが「file」以外であった場合は違う方法になりますが、「file」だった場合は普通のテキストファイルとして用意します。
公式ではパスワードファイルと称されるこのファイルですが、筆者が扱った案件のほとんどでは「.htpasswd」というファイル名を採用していました。
確実な出典が見つからなかったのですが、この「.htpasswd」という名前は、
- UNIX 系システムではユーザーのアカウント情報一覧が記載されたファイル・それを扱うコマンドが「passwd」
- Apache ではデフォルト設定で「.ht」から始まるファイルへアクセスさせない
という理由から「.htpasswd」が定番となっているのだと考えられます。
以下解説でもパスワードファイルを .htpasswd という名前のファイルとして扱います。
さて、この .htpasswd ですが、定番として二通りの作成方法があります。
- 生成 Web サービスを使用する
- htpasswd コマンドを使用する
前者は世にある Web サービスを用いてファイルの内容を生成してもらうという方法です。
メリットとしては特別なツールを用意しなくても手軽にファイルを用意できる点です。
デメリットとしては第一に、別のサイトにユーザー名とパスワードを入力、そしてほとんどの場合は送信する行為がプロジェクトによっては問題になるかもしれない点です。
また、後述するハッシュアルゴリズムを自由に選択できないケースもあります。
htpasswd コマンドは Apache に同梱されているパスワードファイル作成コマンドで、apache が入っているサーバーではほぼ使えると見てよいでしょう。
メリットはハッシュ方式の選択など細やかな設定が行えることや、必然的に ssh での作業になりがちなので、作成したファイルのアップロード作業が不要な点でしょうか。
デメリットはコマンドラインによる作業になるので、慣れていないと作業難易度が跳ね上がる点でしょう(付録に例を載せておきましたので、頑張れば設定可能だと思います)。
これら方法で作成したファイルの中身は以下のようになっているでしょう。
例:ユーザー名「hoge」パスワード「fuga」
hoge:rw/IAdwM4wZI.
実は同じファイルに複数行の設定を書くと、複数セットのユーザー名:パスワードを設定することができます。
例:ユーザー名「hoge」パスワード「fuga」&ユーザー名「sample」パスワード「test」
hoge:rw/IAdwM4wZI.
sample:fUX9khjeHYo86
さて、生成されたファイルを見ると、「:」で区切られた前半にユーザー名を見ることができますが、後半は乱数のような文字列が並んでいます。
これはパスワードをハッシュアルゴリズムにかけたものなのですが、実は現行の最新である Apache 2.4 系ではデフォルトが MD5 となっているのでセキュリティ強度は若干不安が残ります。
また上記例では Apache 2.2 系の時のデフォルトである crypt 準拠のアルゴリズムを使用していますが、こちらはパスワードを 8 文字までしか受け付けていません。
8 文字以上のパスワードを入力しても 9 文字目以降を削除した上で受け付けられてしまうので、こちらもセキュリティ強度的には不安と言えます。
なので、Apache 2.4 以降を使用している場合は、特別な理由がない限りハッシュアルゴリズムには bcrypt などを用いた方がよいでしょう。
設定フローチャート判定
[1] 使用している Web サーバーソフトウェアは?
- Apache である → このまお読みください。
- Apache ではない → この記事では解説しておりません。
[2] .htaccess は使用できる?
- 使用できるし、.htaccess で設定をしたい → 付録:ディレクティブ設定(1)をコピぺし、適当なテキストエディタで編集の用意をして 3 に進む
- 使用できないが、httpd.conf を制御できるかつ、ssh などでデーモンを制御できる → 付録:ディレクティブ設定(2)をコピぺし、適当なテキストエディタで編集の用意をして 3 に進む
- .htaccess も httpd.conf も使用できない → Apache では設定不可です。
[3] AuthuserFile ディレクティブのため、ベーシック認証をかける対象ディレクトリの絶対パスを調べたいが、以下の方法のうち、使用できる方法は?
- ssh でログインしている → cd で対象ディレクトリに移動した後、pwd コマンドで調べてたパスにパスワードファイル名を結合して使用。 4 へ進む
- PHP が使用可能 → 付録:絶対パス作成検証 PHP ファイルを使用して得たパスにパスワードファイル名を結合して使用。 4 へ進む
- FTP ソフトで接続している → ソフトから取得できる接続先の絶対パスにパスワードファイル名を結合して使用。 4 へ進む(サーバーによっては動作しない可能性あり)
- サーバー会社 or サーバー管理者から提供されている → そのままパスワードファイル名を結合して使用。 4 へ進む
- 上記いずれも不可 → サーバー会社 or サーバー管理者に問合せる必要があるが、そもそも非公開情報の可能性あり
[4] 次のうち、サーバーとの接続に使用できるソフトウェアは?
- CLI (ターミナルやコマンドプロンプト)で SSH 接続が可能だし、使うのは慣れている → vi コマンドなどで .htaccess or httpd.conf を編集。httpd.conf を編集した場合はデーモンを再読み込み。 5 に進む
- FTP で接続ができる → ローカル PC のエディタで .htaccess or httpd.conf を作成し、上書きアップロード。httpd.conf を編集した場合はデーモンを再読み込み。 5 に進む
[5] パスワードファイルを作成するために使用するツールは?
- SSH が使用できない or 手軽さ重視 → Google などで「htpasswd」と検索して出てくる自動生成系サイトでユーザー名とパスワードを入力、内容をファイルとして保存する
- 作業を内部で完結させたい or ハッシュアルゴリズムを自身で選びたい → 付録:htpasswd のコマンド例 を参考に htpasswd コマンドを使用し、直接作成
[6] 実際にブラウザで確認
- 問題なくベーシック認証が掛かっている → お疲れ様でした
- エラー発生 → 設定を元に戻してから 1 に戻る
- それでもうまくいかない → ブラウザがキャッシュを持っている可能性があるので、シークレットウィンドウやプライベートウィンドウを用いて再度アクセスを試みる
付録: ディレクティブ設定
(1) .htaccess 用
AuthName "hoge" AuthType Basic AuthUserFile ここに絶対パスが入ります require valid-user
(2) httpd.conf 用
<Directory ここに対象ディレクトリの絶対パス末尾スラッシュ無し$gt; AuthName "hoge" AuthType Basic AuthUserFile ここに絶対パスが入ります require valid-user </Directory>
付録: 絶対パス作成検証 PHP ファイル
PHP 5 系以上なら動作します。下記をコピぺしただけだと誰でもアクセスできる状態なので、ファイル内コメントにあるように IP アドレスによるブロックをかけてから使用してください。
<?php // ここに // https://www.cman.jp/network/support/go_access.cgi // などで調べられるクライアント IP を設定すると、その IP 以外からのアクセスを遮断します // 空文字にしておくと誰でもこのファイルにアクセスできますが、フルパス情報が盗まれる可能性があります $ip = ''; // 不正アクセスを弾く if ($ip !== '' && ( ! isset($_SERVER['REMOTE_ADDR']) || $_SERVER['REMOTE_ADDR'] !== $ip)) { die(); } //このファイルが置かれているディレクトリの絶対パスを出力する // DIR と同義 echo dirname(FILE);
付録: htpasswd のコマンド例
例えば
- /var/www/html/example ディレクトリにファイル作成
- ファイル名は .htpasswd という
- ユーザー名は hoge
という条件なら、以下のようなコマンドになります。
htpasswd -c /var/www/html/example/.htpasswd hoge
上記はファイルを新規作成時に使用するコマンドで、既にある場合は -c を抜いて
htpasswd /var/www/html/example/.htpasswd hoge
で問題ないです。
また、 -B オプションを付けることで bcrypt でハッシュ化することが可能です。
htpasswd -c -B /var/www/html/example/.htpasswd hoge
このコマンドを入力するとパスワード入力を二回求められるので、同じパスワードを二回打ち込みましょう(打ったパスワードは画面に表示されないので注意)。
後書き
いざ調べてみると案外奥が深い……というか前提知識が結構複雑だというのが正直な感想です。
ですが、個人的に比較的遭遇率の高い作業なので、いつかは深堀しなくてはならないと思っていました。
勉強のついでに記事を書いたようで申し訳ないですが、自分と同じ事を勉強しようとしている人の助けになれば幸いです。
プログラマー/N.Go