より良い方法があるように感じるので、提案を出してみましょう。多くのコメントを歓迎します。
要件#
国内のネットワーク環境では、ウェブサイトの TLS セキュリティ証明書が DNS の問題により、オンライン証明書ステータスプロトコル(OCSP)サーバーに正常にアクセスできない場合があります。OCSP は、ネットワーク上の SSL/TLS 証明書の失効状態をリアルタイムで検証する役割を担っています。
例えば、curl コマンドラインツールを使用して、schannel(Windows のネイティブ TLS ライブラリ)を使用しているウェブサイトにアクセスしようとすると、証明書のステータスがオンラインで検証できないため、次のようなエラーが発生する可能性があります:
私はここで digicert の証明書を使用しています。ocsp サーバーは ocsp.digicert.com です。このサーバーがブロックされたりアクセスできない場合、証明書のステータスの検証に失敗します。Let's Encrypt の証明書を使用したことがある方は、さらに深刻な影響を受けるかもしれません〜〜
$ curl -v https://hub.x-cmd.com
* Trying 47.113.155.92:443...
* Connected to hub.x-cmd.com (47.113.155.92) port 443 (#0)
* schannel: disabled automatic use of client certificate
* ALPN: offers http/1.1
* schannel: next InitializeSecurityContext failed: Unknown error (0x80092013) - 吊り消しサーバーがオフラインになっているため、吊り消し機能を確認できません。
* Closing connection 0
* schannel: shutting down SSL/TLS connection with hub.x-cmd.com port 443
curl: (35) schannel: next InitializeSecurityContext failed: Unknown error (0x80092013) - 吊り消しサーバーがオフラインになっているため、吊り消し機能を確認できません。
ps. schannel バージョンの curl はデフォルトで「証明書ステータスの検証」を有効にしますが、openssl バージョンの curl は有効にしません。そのため、openssl バージョンの curl は検証に失敗しても正常に結果を取得できます。
解決策の概要:OCSP ステープリングの導入#
OCSP ステープリングは、オンライン証明書のチェックを最適化する方法であり、ウェブサイトサーバーがクライアントの代わりに事前に OCSP 認証結果を取得し、「ステープル」として提供することで、各クライアントが OCSP サーバーに個別にクエリを送信する必要を回避します。これにより、ハンドシェイクプロセスの遅延が減少し、OCSP サーバーの負荷も軽減されます。
OCSP ステープリングの動作原理#
従来のモードでは、クライアントは TLS ハンドシェイク中に CA の OCSP サーバーと追加のラウンドトリップ通信を行い、サーバー証明書の有効性を確認する必要がありました。OCSP ステープリングは、次の方法でこのプロセスを簡素化しました:
- TLS ハンドシェイクリクエスト時に、クライアントはサーバーに証明書ステータスの提供を要求します。
- サーバーは定期的に CA の OCSP サーバーから署名された証明書ステータスレスポンスを取得し、キャッシュします。
- 新しい TLS ハンドシェイクリクエストがある場合、サーバーはキャッシュされた OCSP レスポンスを証明書と一緒にクライアントに「ステープル」として送信します。
このメカニズムにより、クライアントは直接 OCSP サーバーにリクエストを送信する必要がなく、TLS ハンドシェイクの遅延が減少します。また、OCSP サーバーの負荷も軽減されます。なぜなら、OCSP サーバーは各クライアントリクエストに対して個別のレスポンスを提供する必要がなくなるからです。(また、ocsp サーバーがブロックされる心配もありません)
以下は、このプロセスの簡略化された図です:
以下は、可能性のあるより良い図です:
ocsp ライブラリを使用して OCSP ステープリングを有効にする#
Node.js サーバーで OCSP ステープリングを実装するために、ocsp ライブラリを使用することができます。ただし、このライブラリはメンテナンスされていないため、TypeScript ユーザーはこの prの修正を参考にする必要があるかもしれません。インストールが完了したら、サーバーを以下のように設定できます:
元の Node サーバーに、server の OCSPRequest イベントを追加し、イベント内で OCSP 情報を返すようにします。
import ocsp from "ocsp"
// OCSPのキャッシュを作成します(OCSPステープリングリクエストに応答するために使用されます)
const cache = new ocsp.Cache()
server.on('OCSPRequest', (cert, issuer, cb) => {
ocsp.getOCSPURI(cert, function (err, uri) {
if (err) return cb(err, Buffer.alloc(0))
if (uri === null || uri === undefined) return cb(null, Buffer.alloc(0))
const req = ocsp.request.generate(cert, issuer)
cache.probe(req.id.toString(), function (err, cached) {
if (err) return cb(err, Buffer.alloc(0))
if (cached !== false) return cb(null, cached.response)
const options = {
url: uri,
ocsp: Buffer.from(req.data)
}
cache.request(req.id, options, cb)
})
})
})
問題が発生する可能性#
- サーバーのキャッシュされた OCSP 情報は期限切れになる可能性があり、定期的に更新する必要があります。ocsp ライブラリはこの問題を解決するためにすでに対応しています。
- サーバーと CA の OCSP サーバー間のネットワークに問題が発生する可能性があり、OCSP 情報を取得できない場合があります。この場合、クライアントが OCSP サーバーにリクエストする方法にフォールバックする必要があります。サーバー側は、OCSP 情報を取得できないためにクライアントのリクエストを拒否すべきではありません。
参考資料#
- 提升 TLS 性能 30%?谈谈在 Node.JS 上的 OSCP Stapling 实践 - とても良い記事です。考え方は時代遅れではありませんが、ocsp ライブラリはメンテナンスされていません。
- How to enable ocsp? - 外国のネットワーク環境が良いため、彼らはこの問題に直面しないのでしょうか?情報が本当に少なく、関連するライブラリがないため、ずっと考えていました。