t05-peer-cert.md 3.6 KB


title: "T05. サーバー側でピア証明書を参照する" order: 47

status: "draft"

mTLS構成では、接続してきたクライアントの証明書をハンドラの中で読めます。証明書のCN(Common Name)やSAN(Subject Alternative Name)を取り出して、ユーザーを特定したり、ログに残したりできます。

基本の使い方

svr.Get("/me", [](const httplib::Request &req, httplib::Response &res) {
  auto cert = req.peer_cert();
  if (!cert) {
    res.status = 401;
    res.set_content("no client certificate", "text/plain");
    return;
  }

  auto cn = cert.subject_cn();
  res.set_content("hello, " + cn, "text/plain");
});

req.peer_cert()はtls::PeerCertオブジェクトを返します。boolに変換できるので、まずは証明書の有無を確認してから使います。

取り出せる情報

PeerCertからは、以下の情報を取得できます。

auto cert = req.peer_cert();

std::string cn = cert.subject_cn();        // CN
std::string issuer = cert.issuer_name();   // 発行者
std::string serial = cert.serial();        // シリアル番号

time_t not_before, not_after;
cert.validity(not_before, not_after);      // 有効期間

auto sans = cert.sans();                   // SAN一覧
for (const auto &san : sans) {
  std::cout << san.value << std::endl;
}

証明書がホスト名をカバーしているかを確認するヘルパーもあります。TLSのホスト名検証と同じ規則で、IPアドレスはIPアドレスのSANと、ホスト名はDNS名のSANと照合し、CNはDNS名のSANがないときだけ使います。

if (cert.check_hostname("alice.corp.example.com")) {
  // 一致
}

証明書ベースの認可

CNやSANを使って、ルート単位でアクセス制御できます。

svr.set_pre_request_handler(
  [](const httplib::Request &req, httplib::Response &res) {
    auto cert = req.peer_cert();
    if (!cert) {
      res.status = 401;
      return httplib::Server::HandlerResponse::Handled;
    }

    if (req.matched_route.rfind("/admin", 0) == 0) {
      auto cn = cert.subject_cn();
      if (!is_admin_cn(cn)) {
        res.status = 403;
        return httplib::Server::HandlerResponse::Handled;
      }
    }

    return httplib::Server::HandlerResponse::Unhandled;
  });

Pre-requestハンドラと組み合わせれば、共通の認可ロジックを一箇所にまとめられます。詳しくはS11. Pre-request handlerでルート単位の認証を行うを参照してください。

SNI(Server Name Indication)

SNIはクライアントが自動で送ります。サーバー側では、req.sni()でクライアントが指定したサーバー名を取得できます。ただしSSLServerはその名前に関係なく同じ証明書を提示するので、1つのサーバーで複数のドメインをホストするなら、すべてのドメインをカバーする証明書を使ってください。名前ごとに証明書を切り替えたい場合は、TLSバックエンドの機能を直接使います。たとえばOpenSSLなら、コンテキストのセットアップ用コールバックを受け取るSSLServerのコンストラクタで、servernameコールバックを設定します。

Warning: req.peer_cert()は、mTLSが有効で、かつクライアントが証明書を提示した場合のみ有効な値を返します。通常のTLS接続では空のPeerCertが返ります。使う前に必ずboolチェックしてください。

mTLSの設定方法はT04. mTLSを設定するを参照してください。