🔏

クライアント証明書によるアクセス制限

2023/04/15に公開

※本記事は過去の自分用のメモ書きを一部編集、追記しているため分かりづらい部分があります。その点についてはご容赦ください。

証明書を持つユーザーだけが閲覧可能なウェブサイト

しかし特定の人に制限をかけたサイトを作りたいこともある。そのような場合にアクセス制限をかけることがある。

通常のウェブサイトは、ウェブブラウザがあれば世界中から閲覧することが可能。

Basic認証

代表例としてBasic認証によるアクセス制限。ブラウザにIDとパスワードを入力すると閲覧可能となる。

Basic Authentication

しかしID/パスワードのみによる認証は、昨今ではセキュリティとしては高くなく、機械的に連続でリトライすることで突破できてしまう可能性がある。

IPアドレス制限

次にIP制限所定のIPアドレスからのみに許可する。

IP制限は現在でもわりとよく使われている。が、完全なセキュリティを目指す場合、パケット偽装によりそれを突破できてしまうこともある。また、アクセス基のIPアドレスが不定期に変更される場合、毎度それを登録し直す手間もかかる。

クライアント証明書による制限

所定の証明書を所有しているユーザーのみ許可する方法。

管理者から配布された証明書をブラウザに登録することで、その証明書が有効な間は閲覧することができる。証明書を持たないアクセス、または証明書が無効な場合は 400 Bad Request として表示される。

欠点として、証明書ファイルは複製可能なので、利用者以外に流出した場合、予期しない第三者が閲覧する可能性がある。

前述の制限との組み合わせをすることで、より強固になる。

クライアント証明書は、クライアントサーバー上の認証局で発行された証明書ファイルを使用して認証する。証明書には有効期限が設けられているため、期限が切れると利用できなくなる。また、期限切れ前でも管理者によって無効化されることもあり、その場合、それ以降閲覧できなくなる。

_2021-04-09_9.46.58.png

クライアント証明書による制限環境の作成

今回は以下の構成を用いて構築するものとする。

プロダクト
認証局および証明書 OpenSSL
Webサーバー nginx

認証局の作成

いわゆるオレオレ証明書を発行するために、認証局の作成と、そこからクライアント証明書を発行する。

ここでは認証局作成時のCSRは下記のようにした

項目
Country Name(C) JP
State or Province Name(ST) Tokyo
Locality Name(L) Chiyoda
Organization Name(O) anon5r.dev
Organization unit or division name(OU) Development
Common Name(CN) anon5r.dev
emailAddress webmaster@anon5r.dev

これを作成時のsubjectとして記述すると以下の様になる

C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon5r.dev/emailAddress=webmaster@anon5r.dev
subject="/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon5r.dev/emailAddress=webmaster@anon5r.dev"

cd /etc/pki/CA/

openssl req -new -x509 -days $expiry -key ca-priv-anon5r-dev.key -out ca-priv-anon5r-dev.crt -subj $subject
mv ca.key ca-anon5r-dev.key

openssl req -new -x509 -days 3650 -key ca-anon5r-dev.key -out ca-anon5r-dev.crt -subj $subject
openssl ca -name CA_default -gencrl -keyfile ca.key -cert ca.crt -out ca.crl -crldays 730

openssl ca -name CA_default -gencrl -keyfile ca-anon5r-dev.key -cert ca-anon5r-dev.crt -out ca-anon5r-dev.crl -crldays 3650
diff -U0 <(openssl x509 -noout -modulus -in ca-anon5r-dev.crt) <(openssl rsa -noout -modulus -in ca-anon5r-dev.key)
diff -U0 <(openssl x509 -noout -modulus -in ca-anon5r-dev.crt) <(openssl rsa -noout -modulus -in ca-anon5r-dev.key)

クライアント証明書の作成

クライアント発行時のCSRは下記の様に設定
Common NameやemailAddressは発行するユーザー毎に変更

項目
Country Name(C) JP
State or Province Name(ST) Tokyo
Locality Name(L) Chiyoda
Organization Name(O) anon5r.dev
Organization unit or division name(OU) Development
Common Name(CN) user
emailAddress user@anon5r.dev

これを作成時のsubjectとして記述すると以下の様になる

/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=user/emailAddress=user@anon5r.dev

今回は /etc/nginxの直下にあるcerts以下に配置するようにして出力
また管理上の都合で発行する各ユーザーのメールアドレスベースでディレクトリ配置し、その中弐出力するようにした

clientEmail=developer@anon5r.dev
subject="/C=JP/ST=Tokyo/L=Chiyoda/O=anon5r.dev/OU=Development/CN=anon/emailAddress=$clientEmail"
expiry=365 # 1年間有効

cd /etc/nginx/certs/client/anon5r.dev/
mkdir -p users/$clientEmail

openssl genrsa -out users/$clientEmail/user.key 4096
openssl req -new -key users/$clientEmail/user.key -out users/$clientEmail/user.csr -subj $subject

openssl x509 -req -days $expiry -in users/$clientEmail/user.csr -CA /etc/nginx/certs/client/anon5r.dev/ca.crt -CAkey /etc/nginx/certs/client/anon5r.dev/ca.key -CAcreateserial -CAserial ../ca.seq -out users/$clientEmail/user.crt

openssl pkcs12 -export -clcerts -in users/$clientEmail/user.crt -inkey users/$clientEmail/user.key -out users/$clientEmail/user.p12

# nginxを再起動
sudo systemctl restart nginx.service

おまけ

出力先の階層が深いのと、自分しか使わない環境だったので、発行したクライアント証明書をサッと取得できるように、ホームディレクトリにシンボリックリンクを作成

ln -s /etc/nginx/certs/client/anon5r.dev/users/$clientEmail ~/client-certs
mkdir -p ~/usercerts
cp ~/client-certs/*.(crt|csr|key|p12) ~/usercerts/

クライアント証明書の更新

クライアント証明書の期限が切れたら更新する

clientEmail=developer@anon5r.dev
expiry=365 # 1年間有効

openssl x509 -req -days $expiry -in users/$clientEmail/developer.csr -CA /etc/nginx/certs/client/anon5r.dev/ca.crt -CAkey /etc/nginx/certs/client/anon5r.dev/ca.key -CAcreateserial -CAserial ../ca.seq -out users/$clientEmail/user.crt
openssl pkcs12 -export -clcerts -in users/$clientEmail/user.crt -inkey users/$clientEmail/user.key -out users/$clientEmail/user.p12

# nginxを再起動
sudo systemctl restart nginx.service

あとがき

この記事は元々個人の開発環境を作成したときに、その環境をインターネット越しに公開することにはなる、しかしオープンにはしたくない。

  • 自分だけが簡単に使用する環境を用意したい!
  • 毎回ログイン認証するのも面倒!
  • だけどIP制限ではなくどこからでも使いたい!

というワガママから、最適だと思える環境を作る際のメモ書きでした。

反省点とか思うところとか

ウェブサーバーへのアタックなどは防げないにせよ、必要な人にだけ手間なく公開でき、インターネット越しでもクロールされる心配もない、ということで、個人の開発環境としては割とありだったかなと思っています。

ただし、証明書の期限が切れた際の発行や、一時的に共有したい相手のために証明書を発行する手間や、相手側にも証明書取込の手間がかかるなど、通常行われる手軽なIDパスワード運用と比較すると小難しいところかなという課題もあります。

特に、クライアント証明書の発行/更新はもう少しスマートにできるようにしたい。

Discussion