0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Basic認証とは? なぜ今も使われ続けるのか ― HTTP認証の最小構成を技術者向けに

0
Posted at

はじめに

新しくサービスを作っていて、「dev 環境をとりあえず外から見えないようにしたい」と思ったことはないだろうか。検索エンジンに拾われたくない、URL を知っている関係者以外には触らせたくない。でも、そのためだけに本格的なログイン画面を作るのは大げさすぎる——。

そんなとき、.htaccess に数行、あるいは CDN の設定に十数行を足すだけで「鍵」を掛けられるのが Basic 認証 だ。OAuth や OIDC、JWT が当たり前になった今でも、この 30 年前から変わらない仕組みは現場から消えていない。「古い」「危ない」と雑に語られがちだが、実態は 用途を選べば最適解になる方式である。

この記事を貫くテーマは、次の 2 つだ。

  • コスト軸:Basic 認証は「守る価値 < 認証を作り込むコスト」の場面で勝つ最小ゲート。だから 30 年経っても現役だ。
  • TLS 軸:Base64 は暗号化ではない。だから必ず TLS(HTTPS)とセットで使い、本番のユーザー認証は OAuth/JWT/セッションに棲み分ける。

「仕組み → なぜ普及したか → 最大の誤解 → 他方式との棲み分け」の順で、自分の環境で正しく選べるところまで一気に整理していく。


1. Basic 認証の仕組み ― 401 チャレンジと Authorization ヘッダ

Basic 認証は、特別なミドルウェアや独自プロトコルではない。HTTP という仕様そのものに最初から組み込まれた認証だ(認証の枠組みは RFC 7235、Basic 方式は RFC 7617 で定義。元をたどれば 1996 年の HTTP/1.0 まで遡る)。

仕組みは「サーバーが鍵を要求し、クライアントが鍵を差し出す」という 1 往復だけ。これを チャレンジ・レスポンス(challenge-response)と呼ぶ。

順を追うとこうなる。

  1. クライアントが資格情報なしで GET /secret を投げる
  2. サーバーは「ここは認証が要る」という意味で 401 Unauthorized を返す。このとき WWW-Authenticate: Basic realm="dev" というヘッダで「Basic 方式で来い」と指示する(realm は認証領域の名前で、ブラウザのダイアログに表示される)
  3. ブラウザはユーザーにダイアログを出し、入力値を組み立てて再リクエスト。curl なら -u user:pass で最初から付けられる
  4. サーバーが資格情報を確認して 200 OK

ポイントは Authorization ヘッダの中身だ。Basic 認証では、ユーザー名:パスワードBase64 でエンコードしただけの文字列を送る。

$ printf 'user:pass' | base64
dXNlcjpwYXNz

# 実際に飛ぶヘッダ
Authorization: Basic dXNlcjpwYXNz

もうひとつ重要なのが ステートレスであること。サーバーはログイン状態を覚えていない。クライアントが毎回のリクエストに Authorization ヘッダを付け続けることで認証が成立する。この「サーバーが何も覚えない」性質が、次章の「普及した理由」と、第 3 章の「本番に向かない理由」の両方の根っこになる。


2. なぜ今も使われ続けるのか ― 普及を支える 6 つの理由

「こんなに単純な方式が、なぜ OAuth 全盛の今も生き残っているのか」。これは懐古趣味ではなく、技術的な必然がある。

理由 中身
HTTP 標準の一部 追加プロトコルもライブラリも不要。ブラウザ・curl・あらゆる HTTP クライアント・プロキシがビルトイン対応。
ステートレス サーバーにセッションストア(Redis 等)が要らない。水平スケール・CDN・ステートレス関数と好相性。
実装コストほぼゼロ .htaccess / htpasswd、nginx 数行、CDN エッジ関数十数行。新規コードをほぼ書かずに塞げる。
インフラ層で完結 リバプロ・CDN・API Gateway だけで掛けられ、アプリ本体は無改修。
枯れた仕様 30 年動いており、解釈の揺れ・実装差がほぼない。ハマりどころが少ない。
自動化と相性が良い ヘッダ 1 本で完結。監視・CI のスクリプトから叩きやすい。

順に補足していく。

(1) HTTP 標準の一部だから、何も足さなくていい。 「ライブラリを入れて SDK を初期化して……」という手順がゼロ。ブラウザも curl も最初から喋れる。

(2) ステートレスだから、スケールで悩まない。 サーバーがセッションを保持しないので、リクエストはどのインスタンスに飛んでも成立する。CDN やサーバーレス関数のように「状態を持ちたくない」基盤と特に相性が良い。

(3) 実装コストが限りなくゼロ。 Apache なら .htaccess.htpasswd、nginx なら auth_basic 数行で済む。しかも .htaccessディレクトリ単位で掛けられるので、「この管理画面ディレクトリだけ鍵」といった粒度も簡単だ。

(4) インフラ層だけで完結する。 これが地味に効く。リバースプロキシ・CDN のレイヤで掛けられるため、アプリ本体に一切手を入れず、配信設定だけで「とりあえず非公開」にできる。

(5) 仕様が枯れている。 30 年動いている仕様なので、「このブラウザだけ挙動が違う」といった事故が少なく、安心して使える。

(6) 自動化と相性が良い。 ヘッダ 1 本で完結するので、ヘルスチェックや CI からのアクセスが楽だ。リダイレクトを伴うフォームログインや、複数往復が必要な OAuth フローは、ここが地味に面倒になる。

まとめると、Basic 認証は 「守るべき価値 < 認証を作り込むコスト」な場面で圧勝する。本番のユーザー認証では他に負けるが、開発環境のゲート・社内ツール・プロトタイプでは今でも第一選択になる。


3. 最大の誤解 ― Base64 は暗号化ではない

ここが、Basic 認証で一番誤解されているポイントだ。Authorization: Basic dXNlcjpwYXNz という一見ランダムな文字列を見て、「暗号化されているから安全」と思ってしまう人がいる。これは完全な誤りだ。

Base64 は暗号化ではなく、ただの**エンコード(表現の変換)**でしかない。鍵もパスワードも要らず、誰でも 1 コマンドで元に戻せる。

$ printf 'dXNlcjpwYXNz' | base64 -d
user:pass

このとおり、通信を盗み見た人がいれば一瞬で user:pass が復元される。つまり、TLS(HTTPS)なしの Basic 認証は、パスワードを平文で送っているのと同じだ。

だから鉄則はシンプルだ。Basic 認証を使う経路は、例外なくすべて HTTPS にする。 フロント〜サーバー間だけでなく、リバースプロキシ〜オリジン間も含めて TLS で固める。「Base64 だから一応隠れている」ではなく、鍵(ID とパスワード)は HTTP のままなら丸見えになる——ここが Basic 認証で一番事故が起きるポイントだ。

公共 Wi-Fi では何が起きるか

一番油断しがちなのが 公共 Wi-Fi だ。カフェ・空港・ホテルの無料 Wi-Fi は、同じネットワークにつないでいる第三者から通信が覗ける状態にある。ここで HTTP(平文)の Basic 認証を使っていると、こうなる。

Authorization: Basic ... ヘッダがそのまま電波に乗り、base64 -d 一発で ID とパスワードが抜かれる。これは Wireshark のようなツールで誰でもでき、特別な技術は要らない。

さらに怖いのが、正規の SSID になりすました 偽アクセスポイント(Evil Twin) だ。利用者がそれと気づかず接続すると、攻撃者が通信の真ん中に入り込み(中間者攻撃 / MITM)、やり取りを丸ごと覗ける。

HTTPS なら、たとえ同じ Wi-Fi につながれていても通信全体が暗号化されているので、攻撃者にはヘッダの中身が読めない。だから——「社内だから」「dev 用だから」と HTTP のまま Basic を掛けるのが、実は一番危ない。出先や公共回線を一度でも通る可能性があるなら、例外なく HTTPS だ。

Base64 の誤解以外にも、Basic 認証には構造由来の弱点がある。本番のユーザー認証に使うと、これらが一気に表面化する。

  • ログアウトできない:ブラウザが資格情報をキャッシュし続けるため、「ログアウト」という操作を作れない。
  • 資格情報を毎回送る:ステートレスゆえ全リクエストにパスワード相当が乗る。その分、アクセスログ・プロキシ・APM のトレースに Authorization ヘッダがうっかり残る事故が起きやすい。
  • ブルートフォース耐性がない:仕様自体にレート制限の概念がないので、総当たりは WAF やプロキシ側で別途防ぐ必要がある。
  • MFA も認可粒度もない:多要素認証や「この人はこの操作だけ許可」といった細かい制御はできない。
  • クローラー・ブラウザ依存の制約:検索エンジンのクローラーは認証を越えられないため、本番公開ページに掛けると検索に載らなくなる(逆に dev を隠す用途では好都合だ)。一部のスマホ環境などでダイアログの扱いが弱いこともある。

これらは「Basic がダメな証拠」ではなく、「Basic を本番ユーザー認証に誤用したときに出る症状」だ。用途を守れば問題にならない。


4. 他方式との棲み分け ― どこで Basic、どこで OAuth/JWT/mTLS

では、いつ Basic を使い、いつ他の方式を選ぶのか。「ゲートを掛ける・本人を確かめる」系の代表を、用途で棲み分けるための地図を置く。

方式 レイヤ 強み 弱み 向いている場面
Basic 認証 HTTP ヘッダ 実装ゼロ・ステートレス・全クライアント対応 ログアウト不可・MFA 不可・認可粒度なし dev/staging ゲート、社内ツール、プロトタイプ
Digest 認証 HTTP ヘッダ 平文を送らない MD5・実装が重い・TLS で価値が薄い (新規採用は非推奨)
API Key ヘッダ等 M2M でシンプル、キー単位で失効・レート制限 キー=そのまま秘密、ローテーション運用が要る 外部公開 API、軽量なサービス間認証
Bearer(OAuth2 / JWT) HTTP ヘッダ スコープ・有効期限・失効・認可委譲・SSO・MFA 認可サーバーや検証基盤が必要 本番のユーザー / API 認証の本命
セッション Cookie アプリ ログアウト・セッション管理・UX 自由 サーバー状態 or 署名 Cookie が要る 一般的な Web アプリのログイン
mTLS TLS 証明書で相互認証、ゼロトラスト 証明書の配布・更新が重い サービス間通信、社内ゼロトラスト

ここで Digest 認証にも触れておく。Digest は Basic の弱点(パスワードがそのまま飛ぶ)を、サーバーが渡す使い捨ての値(nonce)を使ったハッシュで解消しようとした方式だ。理屈の上では Basic より安全だが、今ではほぼ使われていない。ハッシュが歴史的に MD5 中心で弱いこと、サーバー側が平文パスワード相当を持たないと検証できずパスワードのハッシュ保存と相性が悪いこと、そして何より TLS が普及した今、「平文を送らない」というメリットを TLS がまるごと吸収してしまったこと。結論として、新規で Digest を選ぶ理由はほぼなく、「Basic + 必ず TLS」が現実解だ。

方式選びの原則は、次の 3 つに集約できる。

  • 人間が日常的にログイン/ログアウトする → セッション Cookie か OAuth2/OIDC
  • API・サービス間(M2M)の通信 → Bearer トークン(OAuth2 Client Credentials)/ API Key / mTLS
  • 「中身を作り込む前に、とにかく外から見えないように塞ぎたい」Basic 認証

実務でいちばん多い形が、**「dev は Basic でゲート、prod は公開」**という棲み分けだ。

  • dev / staging には、CDN のエッジ関数などで Basic 認証を掛け、資格情報なしのアクセスは 401 で弾く
  • 公開すべき本番(LP など)は認証なしでオープン
  • 本番のユーザー認証・API 認証は OAuth/OIDC や JWT に任せ、Basic は dev の目隠しに徹する

CDN のエッジ関数で書くなら、こんな数行だ(CloudFront Functions の例)。

function handler(event) {
  var request = event.request;
  var headers = request.headers;
  var expected = "Basic " + "ZGV2dXNlcjpzM2NyZXQ=";  // base64("devuser:s3cret")

  if (!headers.authorization || headers.authorization.value !== expected) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: 'Basic realm="dev"' } }
    };
  }
  return request;  // 認証 OK → そのままオリジンへ
}

nginx なら、もっと馴染みのある形になる。

location / {
    auth_basic           "dev";
    auth_basic_user_file /etc/nginx/.htpasswd;   # htpasswd -cB で生成
}

最軽量で掛けられるのが効くわけだが、運用の注意点もある。資格情報を関数に直書きすると、変更のたびに再デプロイが必要になる。頻繁にローテーションするなら、外部のキー値ストアに逃がして再デプロイ不要にする、といった一工夫を入れる。また、複数サーバーやエッジに同じ .htpasswd を配る構成では、資格情報の配布と同期が地味な運用コストになる。「WAF や専用の認証基盤を入れるほどではない」ことを確認したうえで選ぶのがコツだ。


おわりに

Basic 認証は、「とりあえず塞ぐ」の最適解だ。万能だから生き残っているのではなく、コストと守りたい価値が釣り合う一点で、他のどの方式よりも軽いから生き残っている。本命の認証にたどり着くまでの「仮ゲート」として割り切って使うのが、いちばん筋の良い付き合い方だろう。

最後に、二本柱をもう一度。

  • コスト軸:Basic は「守る価値 < 作り込むコスト」の場面で勝つ最小ゲート。dev の目隠し・社内ツール・プロトタイプでは今でも第一選択だ。
  • TLS 軸:Base64 は暗号化ではないので、必ず HTTPS とセットで。本番のユーザー認証は OAuth/JWT/セッションに棲み分ける。

もし「本番のユーザーログインを作りたい」「API をスコープ付きで守りたい」というフェーズに進むなら、次に学ぶべきは OAuth 2.0 / OpenID Connect / JWT だ。Basic 認証で「HTTP 認証ってこう動くのか」という土台を掴んでおくと、その先の理解がぐっと速くなるはずだ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?