セキュリティ●●●●○

mTLS

mTLS ・ えむてぃーえるえす

サーバだけでなくクライアントも証明書と秘密鍵で身元を証明する TLS。サービス間通信の要。

✦ 30秒で試す — 航海サービス同士も、互いに身分証を見せ合ってから話す攻撃者は、注文サービスに成りすませる?予想してみる

概要

mTLS(mutual TLS、相互 TLS)は、通信の両端、つまりサーバだけでなくクライアントも証明書を提示し合い、互いの身元を確かめる TLS の使い方です。私たちが普段ブラウザで使う HTTPS では、証明書で名乗るのはサーバ側だけで、クライアント(ブラウザ)は匿名のまま接続し、利用者の確認はその後にパスワードなどで行います。mTLS はこれを対称にし、「接続してきた相手が名乗りどおりの相手か」を、接続を確立する時点で暗号の仕組みによって双方向に保証します。

登場するのは、人ではなく機械どうしの通信です。マイクロサービスどうしの呼び出し、銀行の API のような企業間の連携、IoT 機器とサーバの接続など、「パスワードを打ち込む人間がいない」通信で、なりすましと盗聴を同時に防ぐ標準的な手段になっています。

大事なのは、身元の証明の中身です。証明書は公開情報なので、見せるだけなら誰にでもできます。mTLS で本人だと認められるのは、「証明書に書かれた公開鍵と対になる秘密鍵を持っていて、それでハンドシェイクの記録に署名できる」者だけです。

なぜ生まれたか

クライアント証明書の仕組み自体は、SSL 3.0(1996年)の時代から TLS の仕様にありました。しかし Web ではほとんど使われませんでした。一般の利用者に証明書を配り、端末ごとに管理させるのは現実的でなく、利用者の認証はパスワードとセッションで足りたからです。

サーバ側では長く「境界の内側は信頼する」という考え方が主流でした。ファイアウォールの内側に入れば、サービスどうしは互いを疑わず平文で話す、城と堀のモデルです。このモデルは、攻撃者が一度内側に入り込めば横へ自由に動けるという弱点を抱えていました。クラウドへの移行とマイクロサービス化で内側の通信が爆発的に増え、その経路がデータセンターや事業者をまたぐようになると、「どのネットワークから来たか」では相手を判断できなくなります。

そこで、ネットワーク上の場所を信頼の根拠にしないゼロトラストの考え方が広がり、その土台として「通信ごとに相手の身元を暗号で確かめる」mTLS が見直されました。2017年に登場した Istio などのサービスメッシュが証明書の配布と更新を自動化したことで、何百ものサービスの間で mTLS を張ることが現実的になります。金融分野でも、OAuth のクライアント認証とアクセストークンを証明書に結び付ける仕様(RFC 8705、2020年)が作られ、銀行 API の標準に取り入れられています。

詳細

一方向 TLS と mTLS の違い

mTLS は新しいプロトコルではなく、TLS ハンドシェイクのオプションを有効にしたものです。通常の TLS では、サーバが証明書と署名を提示し、クライアントは公開鍵暗号の仕組みでその正当性を確かめますが、逆向きの確認はありません。mTLS では、サーバがハンドシェイクの途中で「あなたの証明書も見せてください」と要求し、クライアントも証明書と、対応する秘密鍵で作った署名を送ります。

もうひとつの違いは、信頼の起点です。公開 Web のサーバ証明書は、ブラウザや OS があらかじめ信頼している公開の CA(認証局、証明書に署名して身元を保証する機関)が発行します。mTLS のクライアント証明書は、ふつう組織の中に立てたプライベート CA が発行し、サーバは「うちの CA が署名した証明書だけを仲間と認める」ように設定します。組織の中だけで通用する身分証の仕組みを作る構図です。

一方向 TLS(ふつうの HTTPS)公開の CAブラウザが最初から信頼発行ブラウザ匿名のままサーバ証明書+秘密鍵サーバ → ブラウザ: 証明書と署名ブラウザ → サーバ: 何も名乗らない利用者の確認はログイン画面で後から行うmTLS(サービスどうし)プライベート CA組織の中で運用発行発行注文サービス証明書+秘密鍵決済サービス証明書+秘密鍵双方が証明書と署名を見せ合う同じ CA が署名した証明書だけを仲間と認める接続した時点で相手のサービス名が分かる
一方向 TLS では身元を示すのはサーバだけ。mTLS では同じプライベート CA が両端に証明書を配り、双方が証明書と署名を見せ合う

TLS 1.3 のハンドシェイクを順に追う

TLS 1.3 での流れは次のとおりです。(1)クライアントが ClientHello で接続を始め、鍵共有に使う一時的な値を送ります。(2)サーバは ServerHello で自分の値を返し、ここで両者は共通の鍵を導き出します。ここから先のメッセージは暗号化されます。(3)サーバは CertificateRequest(証明書の要求)、自分の証明書、CertificateVerify(ここまでのやり取りの記録に、自分の秘密鍵で付けた署名)、Finished を送ります。(4)クライアントはサーバ証明書と署名を検証したうえで、自分の証明書、自分の秘密鍵による CertificateVerify、Finished を返します。(5)サーバはクライアント証明書を検証し、署名を証明書の公開鍵で確かめて、はじめて接続を受け入れます。

サーバクライアントサーバクライアントここから先は暗号化されるサーバ証明書と署名を検証チェーン・期限・署名を検証ClientHello・鍵共有の値ServerHello・鍵共有の値CertificateRequest 証明書の要求Certificate サーバ証明書CertificateVerify・FinishedCertificate クライアント証明書CertificateVerify 秘密鍵による署名Finishedアプリケーションのデータ

TLS 1.2 では順序が少し違い、サーバは自分の証明書を送った後で CertificateRequest を送り、クライアント証明書も暗号化されずに流れていました。TLS 1.3 では証明書が暗号化されるので、経路上から「どのクライアントが接続したか」を覗かれにくくなっています。

肝心なのは CertificateVerify です。署名の対象は、その接続でしか生まれない乱数を含むハンドシェイクの記録なので、過去の署名を使い回すこともできません。証明書を盗み見た者は証明書の提示までは真似できても、この署名は作れません。下の部品で、クライアントの手札を自分の手でサーバへ運んでください。証明書だけを盗んだ攻撃者や、期限の切れた証明書に切り替えると、サーバの検証がどの門で止まるかが分かります。

⚡ 体験: mTLS の握手を自分の手で進める
立場
証明書を発行証明書を発行プライベート CA組織の中の認証局注文サービスクライアント決済サービスサーバ(検証する側)クライアント証明書秘密鍵ClientHello決済サービスが順に確かめるCA の署名発行元は仲間か有効期限期限内か所有の証明鍵の持ち主か
あなたが進めた順に描かれるシーケンス図(TLS 1.3)
クライアント(注文)サーバ(決済)まだ何も送っていません

あなたは注文サービス(クライアント)です。決済サービスと mTLS で接続します。まず ClientHello をつかんで、決済サービスへ運んでください。

サーバは何を確かめ、何を許すのか

クライアント証明書を受け取ったサーバは、まず証明書の署名をたどって、信頼すると決めた CA に行き着くか(チェーンの検証)、有効期限内か、失効していないかを確かめます。ここで信頼する CA を公開の CA まで広げてしまうと、世界中の誰でも取れる証明書で入れてしまうので、mTLS の信頼の起点は自組織のプライベート CA に絞るのが原則です。

そのうえで、証明書に書かれた身元を取り出します。サービスどうしの mTLS では、証明書の SAN(別名の欄)に spiffe://example.org/ns/prod/sa/order のようなワークロードの識別子を入れる SPIFFE という標準がよく使われます。ここまでが認証で、「注文サービスからの呼び出しだけを決済サービスに許す」といった判断は、その身元に基づく認可として別に書きます。mTLS を張っただけで「つながった相手は何をしてもよい」と扱うのは、よくある設計の穴です。

サービスメッシュとサイドカーによる自動化

mTLS の理屈は単純ですが、すべてのサービスに証明書の取得・検証・更新のコードを書かせるのは現実的ではありません。そこでサービスメッシュ(Istio や Linkerd など)がこの仕事を引き受けます。各サービスの隣にサイドカーと呼ばれるプロキシを置き、サービス間の通信をすべてそこに通すと、アプリケーションは平文で話しているつもりでも、サイドカーどうしが自動で mTLS を張ります。

証明書の発行も自動です。メッシュのコントロールプレーンが CA として働き、Kubernetes 上の各ワークロードに「あなたはこのサービスである」という短命の証明書を配り続けます。これにより mTLS は暗号化と認証にとどまらず、サービス単位の認可ポリシーの土台になります。ゼロトラストの文脈で mTLS が語られるのは、「通信ごとに身元を確かめ、身元に基づいて許可する」基盤になるからです。

運用の本丸は証明書のライフサイクル

mTLS の導入で難しいのは暗号ではなく運用です。証明書には有効期限があり、発行・配布・更新・失効を回し続けなければなりません。期限切れの証明書による大規模障害は業界の定番の事故で、「守りの仕組みが自分のサービスを止める」形で現れます。漏れた証明書を無効にする失効の仕組み(CRL や OCSP)は伝統的に運用が重いため、いまは証明書の寿命を数時間〜数日と極端に短くし、失効を待たずに自然に切らせる方向に進んでいます。短命の証明書は自動更新が前提になるので、mTLS の成否は「証明書の発行と更新をどれだけ人手から切り離せるか」で決まります。SPIFFE の実装である SPIRE や、Kubernetes の cert-manager が重視されるのはこのためです。

もうひとつの落とし穴は、途中で TLS を終端する機器です。ロードバランサやリバースプロキシで TLS をいったん解くと、その先のアプリケーションにはクライアント証明書が届きません。証明書の情報をヘッダに載せて転送する構成はよくありますが、そのヘッダを外部から偽造できないようにしておかないと、確かめたはずの身元が書き換えられてしまいます。

使いどころの目安として、社内のサービスどうしや機械どうしの通信で相手の身元を保証したいなら、mTLS は第一候補です。一方、不特定多数の人が使う一般向けの Web には向きません。そちらは今も、サーバ認証だけの TLS に、パスワードやパスキー、OAuth などのアプリケーション層の認証を組み合わせて守っています。

試問

注文サービス(クライアント)が、決済サービス(サーバ)と相互に証明書を確かめ合う mTLS で接続します。手札のチップ(ClientHello・証明書・ここまでの記録)をドラッグして運ぶと握手が進み、決済サービスは「CA の署名」「有効期限」「所有の証明」の3つの門で順に確かめます。上のボタンで立場(正規のクライアント / 証明書だけ盗んだ攻撃者 / 期限切れの証明書)を切り替えられます。

✦ 命題挑戦中

正規の注文サービスとして mTLS の接続を確立し、同じ証明書の写しを持つ攻撃者はどの門で止まるかを確かめよ

  • 正規のクライアントとして、3つの門をすべて通って接続を確立する
  • 証明書だけ盗んだ攻撃者として握手を進め、所有の証明の門で止められる
⚡ 体験: mTLS の握手を自分の手で進める
立場
証明書を発行証明書を発行プライベート CA組織の中の認証局注文サービスクライアント決済サービスサーバ(検証する側)クライアント証明書秘密鍵ClientHello決済サービスが順に確かめるCA の署名発行元は仲間か有効期限期限内か所有の証明鍵の持ち主か
あなたが進めた順に描かれるシーケンス図(TLS 1.3)
クライアント(注文)サーバ(決済)まだ何も送っていません

あなたは注文サービス(クライアント)です。決済サービスと mTLS で接続します。まず ClientHello をつかんで、決済サービスへ運んでください。