【入門】認証方式とサーバーの設定をわかりやすく解説

本記事は以下の4つの認証について解説します。

Basic 認証
Form 認証 (Cookie によるセッション管理)
Digest 認証
Bearer 認証 (OAuth, OIDC からのアクセストークンを利用)
スポンサーリンク

初めに

本記事は Web サーバー構築の第5回「Basic/Digest/Form/Bearer 認証」編です。

スポンサーリンク

認証の種類

Web サービスの認証の種類には、主に以下の4つが存在します。

Basic 認証Digest 認証フォーム認証Bearer 認証
パスワードを暗号化して送信
(HTTPS でカバー可能)
××○ (暗号化可能)
パスワードを都度送信しない××
ログインせずにアクセス××△ (匿名)
ログアウト (ログインを無効)××
シングルサインオン××
セッション×××
フォーム認証 (Cookie) と、Bearer (JWT) の比較: https://qiita.com/doyaaaaaken/items/02357c2ebca994160804
スポンサーリンク

Basic 認証とは

Basic 認証とはBasic 認証とは、ユーザー名とパスワードを Base64 エンコードして送信する認証方法です。

Basic 認証の仕組み

Basic 認証は以下の流れで行います。

Basic 認証を nginx で設定

nginx で Basic 認証を行う Web ページを作成してみます。

sudo yum install httpd-tools -y
sudo htpasswd -c /etc/nginx/.htpasswd username
New password:
Re-type new password:
sudo vim /etc/nginx/nginx.conf
    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        auth_basic "closed site";                   # BASIC 認証を有効化
        auth_basic_user_file /etc/nginx/.htpasswd; # .htpasswdファイルのパス
sudo systemctl start nginx

curl で Basic 認証を検証

Basic 認証が必要なことを通知

curl localhost -I
HTTP/1.1 401 Unauthorized
(中略)
WWW-Authenticate: Basic realm="closed site"

レスポンスより、以下の2点が確認できます。

なお、ブラウザでアクセスすると以下のようになります。

ブラウザでアクセスした場合

Basic 認証でリクエスト

curl localhost -u username:password -I
HTTP/1.1 200 OK

正しいユーザー名とパスワードを入力すると、無事アクセスできます。

補足: Authorization リクエストヘッダの内容

curl localhost -u username:password -v >/dev/null
(中略)
> Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

"dXNlcm5hbWU6cGFzc3dvcmQ" がセットされています。

これは、"username:password" を Base64 でエンコードした値です。

echo -n username:password |base64
dXNlcm5hbWU6cGFzc3dvcmQ=

Digest 認証とは

Digest 認証Digest 認証とは、ユーザー名とパスワードをハッシュ化して送信する認証方法です。

Basic 認証で問題となった「ユーザー名とパスワード」が平文で送信されるという問題を解決した認証方式です。

なお、現在は、ほぼ全ての Web サイトが HTTPS で暗号化されているので、Digest 認証はあまり使われません。

(中略) or more rarely Basic access authentication.

These weak cleartext protocols used together with HTTPS network encryption resolve many of the threats that digest access authentication is designed to prevent.

https://en.wikipedia.org/wiki/Digest_access_authentication

Digest 認証の仕組み

Basic 認証との主な違いは、③で送信する認証情報の一部にハッシュ関数を利用することです。

②のレスポンスに含まれる WWW-Authenticate レスポンスヘッダの値

WWW-Authenticate レスポンスヘッダ役割
DigestDigest 認証が必要なことを伝える
algorithmハッシュ関数に利用するアルゴリズム
realmレルム名 (認証が適用される範囲)
qop保護レベル
nonceサーバーがランダムに生成する文字列
https://tex2e.github.io/rfc-translater/html/rfc7616.html

③のリクエストに含む Authorization リクエストヘッダの値

Authorization リクエストヘッダ役割
rspauthハッシュ値 (求め方は後述)
qop保護レベル
cnonceクライアントがランダムに生成する文字列
選択平文攻撃を回避するため
nc (nonce count)クライアントが nonce 値を使って送信した回数
https://tex2e.github.io/rfc-translater/html/rfc7616.html

なお、rspauth は以下の方法でハッシュ値を求めます。

A1 = ユーザ名 ":" realm ":" パスワード
A2 = ":" リクエスト URI ※qop=auth の場合
rspauth = MD5( MD5(A1) ":" nonce ":" nc ":" cnonce ":" qop ":" MD5(A2) )

https://datatracker.ietf.org/doc/html/rfc7616#section-3.4.1
A1: https://datatracker.ietf.org/doc/html/rfc7616#section-3.4.2
A2 (rspauth): https://datatracker.ietf.org/doc/html/rfc7616#page-15
https://wiki.suikawiki.org/n/rspauth%3D%22%22

Digest 認証を nginx で設定

nginx で Digest 認証モジュールは、こちら手順で build する必要があります。

今回は build 省略のため、以下の docker image を利用させていただきます。

Digest 認証できる nginx の docker image を作った - Qiita
個人用に作成したのですが、 DockerHub を軽く検索しても見つからなかったので共有します。(※ 筆者のセキュリティリテラシーは「使うモジュールは最新バージョンにしとくか」程度です。 使用は自…
git clone https://github.com/kamuiroeru/nginx-digest-auth-with-docker
cd nginx-digest-auth-with-docker
htdigest -c .htdigest "secret_area" username
New password: 
Re-type new password:

ユーザーのパスワードを設定します。今回は "password" とします。

curl で Digest 認証を検証

curl localhost:8888 --digest -u username:password -v >/dev/null
** 図①に相当
> GET / HTTP/1.1

** 図②に相当
< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Digest algorithm="MD5", qop="auth", realm="secret_area", nonce="1234567890"

** 図③に相当
> GET / HTTP/1.1
> Authorization: Digest username="username", realm="secret_area", nonce="1234567890", uri="/", cnonce="0987654321", nc=00000001, qop=auth, response="ABCDE", algorithm=MD5

** 図④に相当
< HTTP/1.1 200 OK
< Authentication-Info: qop="auth", rspauth="12345ABCDE", cnonce="0987654321=", nc=00000001

フォーム認証 (Cookie でセッション ID を管理) とは

フォーム認証とはフォーム認証とは、ユーザー名とパスワードを HTML フォームで送信する認証方法です。
HTML フォームを利用したログインフォームの例

認証結果は、Cookie (クライアント[=ブラウザ]に情報を保存する仕組み) で管理します。

Cookie には有効期限を設定でき、ログアウトする際は有効期限切れの Cookie をセットします。

フォーム認証の仕組み

なお、サーバーは Cookie の値 (セッション ID) をデータベース (RDB, KVS) に保存します。

クライアントはログイン済みを示す Cookie をセットすることで、2回目以降のアクセスで再ログインせずに済みます。

フォーム認証を Flask で設定

ログイン処理を実装するために、nginx の代わりに Flask (アプリケーションサーバー) を利用します。

pip3 install flask
vim app.py
from flask import Flask, request, make_response, render_template, redirect

app = Flask(__name__)

#以下の値は本来データベースから取得します
username = "name"
password = "pass"
valid_cookie="1"

@app.route("/") #トップページ
def root():
    if (request.cookies.get("session") == valid_cookie): #ログイン済みならホーム画面へ
        return redirect("/home")
    else: #ログインしてないならログインフォーム画面へ
        return render_template("index.html") 

@app.route("/login", methods=["POST"]) #ログイン処理
def login():
    if (request.form["username"] == username and request.form["password"] == password):
        res = make_response(redirect("/home"))
        res.set_cookie("session","1")
        return res 
    else: #ユーザー名かパスワードが間違ってる場合
        return "login fail", 401

@app.route("/home") #ホーム画面
def home():
    if (request.cookies.get("session") == valid_cookie): #ログイン済みならホーム画面
        return "login success"
    else: #ログインしてないならフォーム画面へ
        return redirect("/")

if __name__ == "__main__":
    app.run()
パスページの内容動作説明
/ログインフォームユーザー名とパスワードを "/login" へ POST
有効な Cookie があると "/home" へ
/loginログイン処理ログインに成功すると Cookie を Set して "/home" へ
ログインに失敗すると、401 エラーを表示
/homeホーム画面有効な Cookie があると、ページを表示
有効な Cookie が無いと、"/" へ
mkdir templates
vim templates/index.html
<form method="POST" action="/login" name="login_form">
  <input type="text" placeholder="username" name="username">
  <input type="password" placeholder="password" name="password">
  <input type="submit" value="login">
</form>
flask run -h 127.0.0.1 -p 8080

curl でフォーム認証を検証

ログインフォームを表示

curl localhost:8080
<form method="POST" action="/login" name="login_form">
  <input type="text" placeholder="username" name="username">
  <input type="password" placeholder="password" name="password">
  <input type="submit" value="login">
</form>

ログインフォームが表示されます。(ブラウザで表示すると以下)

ブラウザで表示した場合

ログインしてログイン後の画面を表示

curl localhost:8080/login -L -d "username=name" -d "password=pass" -b cookie.txt -c cookie.txt -v
** 図③に相当
> POST /login HTTP/1.1
** 図④に相当
< HTTP/1.1 302 FOUND
< Location: /home
< Set-Cookie: session=1; Path=/
** 図⑤に相当
> GET /home HTTP/1.1
> Cookie: session=1
** 図⑥に相当
< HTTP/1.1 200 OK
login success
ブラウザで表示した場合

ユーザー名とパスワードが正しければ、サーバーから Cookie が貰え、ホーム画面 (/home) にリダイレクトされます。

補足:Cookie がある時、無い時

curl localhost:8080/home -L -b cookie.txt
login success
ブラウザで表示した場合
curl localhost:8080/home -L -v
> GET /home HTTP/1.1
*
< HTTP/1.1 302 FOUND
< Location: /
*
> GET / HTTP/1.1
*
< HTTP/1.1 200 OK

<form method="POST" action="/login" name="login_form">
  <input type="text" placeholder="username" name="username">
  <input type="password" placeholder="password" name="password">
  <input type="submit" value="login">
</form>
ブラウザで表示した場合 (Cookie は削除)

Bearer 認証 (+OAuth2.0/OIDC)

Bearer 認証とはBearer 認証とは、アクセストークンを送信する方法です。

認証と認可が分かれていることが特徴の認証方式です。

認可(Authorization・AuthZ)認証(Authentication・AuthN)
目的権限を付与ユーザーを識別および検証
利用例アプリケーションの閲覧権限の付与に利用ログインに利用
具体例
プロトコルOAuth 2.0
・SAML
OpenID Connect (OIDC)
OAuth 2.0 (セキュアな OIDC を推奨)
・SAML
利用するトークンアクセストークン (OAuth 2.0)
アサーション (SMAL)
ID トークン (OIDC)
アサーション (SAML)
https://hogetech.info/security/sso/federation

Bearer 認証の仕組み

API サーバーは認証機能を持ちません。認可されたアクセストークンを元にアクセス許可を判断します。

また、アクセストークンを取得するためには、主に OIDC (推奨) や OAuth 2.0 が利用されます。

Bearer 認証を Authlib で設定

Bearer 認証を Authlib で設定する方法は、以下の記事で紹介しているのでご覧ください。

curl で Bearer 認証を検証

curlBearer 認証を検証する方法は以下の記事で紹介してるのでご覧ください。

関連記事

Web サーバー構築


参考文献

RFC 7616 - HTTP Digest Access Authentication 日本語訳