Files
cpp-httplib/docs-src/pages/ja/cookbook/t05-peer-cert.md
T
yhirose 9bfd2ddf1c Fix README and docs pages that no longer match the code
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.
2026-10-18 10:13:57 -04:00

89 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "T05. サーバー側でピア証明書を参照する"
order: 47
status: "draft"
---
mTLS構成では、接続してきたクライアントの証明書をハンドラの中で読めます。証明書のCN(Common Name)やSAN(Subject Alternative Name)を取り出して、ユーザーを特定したり、ログに残したりできます。
## 基本の使い方
```cpp
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`からは、以下の情報を取得できます。
```cpp
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がないときだけ使います。
```cpp
if (cert.check_hostname("alice.corp.example.com")) {
// 一致
}
```
## 証明書ベースの認可
CNやSANを使って、ルート単位でアクセス制御できます。
```cpp
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でルート単位の認証を行う](../s11-pre-request)を参照してください。
## SNI(Server Name Indication)
SNIはクライアントが自動で送ります。サーバー側では、`req.sni()`でクライアントが指定したサーバー名を取得できます。ただし`SSLServer`はその名前に関係なく同じ証明書を提示するので、1つのサーバーで複数のドメインをホストするなら、すべてのドメインをカバーする証明書を使ってください。名前ごとに証明書を切り替えたい場合は、TLSバックエンドの機能を直接使います。たとえばOpenSSLなら、コンテキストのセットアップ用コールバックを受け取る`SSLServer`のコンストラクタで、servernameコールバックを設定します。
> **Warning:** `req.peer_cert()`は、mTLSが有効で、かつクライアントが証明書を提示した場合のみ有効な値を返します。通常のTLS接続では空の`PeerCert`が返ります。使う前に必ず`bool`チェックしてください。
> mTLSの設定方法は[T04. mTLSを設定する](../t04-mtls)を参照してください。