Bring the READMEs and the docs pages (en and ja) in line with the current header: - Fix the client sample that does not compile, the Transfer-Encoding statement for default headers, the verifier callback comment, the thread pool spawn rule, the sanitize_filename description, and the inverted Accept-Encoding sentence. - Describe recent behaviour: TLS errors behind Read/Write failures, Host validation, Connection: close for a body without length, content_reader rules, refused CONNECT, WebSocketClient proxies, CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT, the SSE event size limit, and the payload limit of the Stream API. - Complete the API references of WebSocket, WebSocketClient and the stream:: helpers, and the SSE reconnect rules. - Fix cookbook pages: keychain loading on every backend, received Close status, a multipart sample that trusted the client's file name, an echo sample that sent binary as text, SNI, pre-request handler scope, stop() with WebSocket handlers, and the thread pool notes. - Restore the mutex in the LLM app's Chapter 5 code.
3.6 KiB
title, order, status
| title | order | status |
|---|---|---|
| T05. サーバー側でピア証明書を参照する | 47 | 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を設定するを参照してください。