yasudacloudの日記

札幌に住むソフトウェアエンジニア

クライアント証明書でWebアプリを運用するHow to

久しぶりにStrapiの話。

最近はあまりキャッチアップしてなかったのですが、どうやら管理画面が現在進行形でレスポンシブ対応中のようです。

↓iPhoneで開くとContent Managerはこんな感じになります。

色々操作してみるとちょっと微妙な部分もあるんですが、まあまあ良い感じです。

Strapiをスマホで操作できるということは、外出先でもデータを閲覧や編集ができるようになるわけですね。現在、個人的に作っているWebサイトがまさにその恩恵が大きいということで、今回はStrapiをクラウドで公開してスマホで安全に操作するための設定を行なっていきます。

セキュリティの施策なのでやってることは結構地味な内容になります・・。

何の設定が必要か?

まず、初めの部分から話をするとStrapiはWeb上にホスティングするOSSのヘッドレスCMSです。Strapiの管理画面にはデフォルトでユーザー認証があり、管理者アカウントじゃないとログインできません。

しかし、Strapiを始めとするヘッドレスCMSをそのままWebに公開することはリスクがあります。管理画面はその名の通り管理者(限られた人)が使うことを前提にしており、不必要にアクセス許可をしてはいけません。OSSという特性上、脆弱性を見つけやすい(攻撃されやすい)という見方もあります。

そこでアクセス制限を設けることでそもそも管理画面に到達させないという形が望ましいわけですね。そのための方法が幾つか考えられます。

ベーシック認証

古来から伝わる簡易認証。最悪突破されても深刻な被害にならないものにかけることがあります。開発段階のWebシステムや個人情報を含まないが公開したくないティザーサイトなど。

今回この方式を没にしたのは、やや脆弱さを感じるのもありますがiPhoneでベーシック認証を入力するのが面倒だからです。

IP制限

鉄板、そこそこの安心感。大人数の運用だとちょっと物足りない。ただ、私は固定IPを持っておらず、VPNやプロキシもないのでこの案も没。

クライアント証明書(mTLS)

今回使う方法。Strapiにアクセスする端末はMacとiPhoneの2つだけで良いので、その2台に証明書をインストールしてサーバー側(Nginx)で証明書がないアクセスを弾きます。

Cloudflare Tunnel

神サービス、モダンなZero Trust。理解が浅いのでニュアンスが難しいんですが、仮想的な専用線という表現でいいんでしょうか。

CloudFlareは好きな企業の一つですし、こちらも有力ではあります。ただ、Strapiを既に別のPaaSでホスティングしているので避けました。

クライアント証明書

というわけで、クライアント証明書を導入します。ざっくりした手順は以下のようになります。

①証明書の作成

②Nginxに設定

③Macに証明書設定

④iPhoneに証明書設定

NginxインストールやSSL化、Strapi構築は以前にも書いているので省略。

証明書の作成

既にNginxにSSL設定しているとして、下記のコマンドでクライアント証明書を作成できます。 gist.github.com

Nginxに設定

Nginx側の設定は以下のように最低限2行でできます。

ssl_client_certificate /etc/nginx/certs/ca.crt;

ssl_verify_client on;

が、しかし。

私のNginxのSSLはCertbotを使ってLet's Encryptの証明書を取得しているため、/.well-known/acme-challenge/から始まるパスはパブリックにアクセスできる必要があります。このパスも証明書必須にしてしまうとCertbotの定期更新のタイミングでエラーになると予想されるため、少し工夫します。

特定のパスのみ証明書不要、という設定できれば良いのですがlocation内ではssl_verify_clientディレクティブは使えないようです。そこで、server直下にssl_verify_client optional;とし、クライアント証明書をデフォルト任意にしてからlocation / 内で証明書がないなら403を返すようにします。

こんな感じ。

ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client optional;
location / {
if ($ssl_client_verify != SUCCESS) {
return 403 "SSL Verify Result: $ssl_client_verify";
}
proxy_pass http://localhost:1337;
}

設定が出来たらNginxを再起動。

Macに証明書設定

先ほど作ったp12ファイルをキーチェーンアクセスに登録し、認証局の方の[信頼]セクションから[常に信頼]に変更。

これでWebアクセスしてみると、証明書の選択ダイアログが出てキーチェーンの情報読み取りの確認が行われ、証明書が問題なければ通るようになります。

私はPCではEdgeを使っているのですが、この選択ダイアログは少し時間を置いたりNginxを再起動すると再度出現してきます。これは結構しんどいので、Edge側の設定で証明書を自動選択できないか調べてみました。

キーチェーンアクセスにある先ほど登録した秘密鍵をダブルクリックし、[アクセス制御]の許可するアプケーションにEdgeを追加すれば良い模様。これはおそらく他のブラウザも同じで、FireFoxで試したところダイアログが表示されなくなりました。

iPhoneに証明書設定

同じく、まず証明書をiPhoneにダウンロードします。証明書はPCと違ってp12だけでなくca.crtも必要です。

手っ取り早いのはパソコンからWeb経由で入れる方法。ローカルPCで動いている適当なWebサーバーかWebアプリ(例えばNext.jsとか)にclient.p12とca.crtファイルを配置し、同じネットワーク上のiPhoneからhttp://192.168.x.x/client.p12 みたいな感じで直リン指定するとダウンロードできます。

で、設定アプリから[一般] -> [VPNとデバイス管理] -> [構成プロファイル]にある証明書をそれぞれインストールします。構成済みプロファイルが2件になっていれば成功です。

↑ca.crtを入れずにp12だけだと上記のように赤字で未検証になります。

あとはSafariから対象のURLを開くだけですが、ブラウザリロードでは反映されなかったので一度Safariを落として開き直したらアクセスできました。

まとめ

他にも色々なセキュリティ施策を検討したいものですが、ひとまずやっておきたかったことが出来たので満足。このStrapiをバックエンドで使って近々Webサイト公開しようと思うのでそちらも後日紹介したいと思います。

確定申告やったりVRアプリ作らないといけないのでなんやかんや忙しいです( ´Д`)y━・~~

先日、Switchから出たポケモンのファイアレッド/リーフグリーンのリメイクやりたいんですが、遊ぶ暇が全然ありません。