I always feel that there is a better way, so I'll throw out some ideas. Welcome to criticize.
Requirements#
In the domestic network environment, the TLS security certificate of a website may not be able to access the Online Certificate Status Protocol (OCSP) server due to DNS issues. OCSP is responsible for real-time verification of the revocation status of SSL/TLS certificates in the network.
For example, when we try to access a website that uses schannel (Windows native TLS library) through the curl command-line tool, we may encounter errors like the following due to the certificate status not being verified online:
I am using a digicert certificate here, and the ocsp server is ocsp.digicert.com. In the case where the server is blocked or cannot be accessed, the certificate status verification fails. If you have used a let's encrypt certificate, you should be more affected by it~~
$ 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) - Revocation function was unable to check revocation because the revocation server was offline.
* 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) - Revocation function was unable to check revocation because the revocation server was offline.
ps. The schannel version of curl will enable "certificate status verification" by default, while the openssl version of curl will not. Therefore, even if the verification fails, the openssl version of curl can still obtain the results normally.
Overview of the Solution: Introducing OCSP Stapling#
OCSP Stapling is an optimized method for online certificate checking. It allows the website server to obtain and "staple" the OCSP authentication result in advance instead of each client querying the OCSP server separately. This not only reduces the delay in the handshake process but also reduces the load on the OCSP server.
Working Mechanism of OCSP Stapling#
In the traditional mode, the client needs to communicate with the CA's OCSP server during the TLS handshake process to confirm the validity of the server certificate. OCSP Stapling simplifies this process in the following ways:
- During the TLS handshake request, the client requests the server to provide certificate status.
- The server periodically obtains the signed certificate status response from the CA's OCSP server and caches it.
- When there is a new TLS handshake request, the server sends the cached OCSP response along with the certificate to the client.
This mechanism avoids the need for the client to directly request the OCSP server, reducing the delay in the TLS handshake. At the same time, it reduces the load on the OCSP server because it no longer needs to provide a response for each client request. (And don't worry about the ocsp server being blocked)
The following is a simplified diagram of this process:
The following is a better diagram from Alibaba Cloud:
Enabling OCSP Stapling using the ocsp library#
We can use the ocsp library to implement OCSP Stapling on a Node.js server. Since the library is no longer maintained, TypeScript users may need to refer to the fixes in this PR. After installation, you can configure the server as follows:
On the basis of the original node server, add the server's OCSPRequest event and return the OSCP information in the event.
import ocsp from "ocsp"
// Create cache for OCSP (it'll be used to respond to OCSP stapling requests)
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)
})
})
})
Potential Issues#
- The cached OCSP information on the server may expire and needs to be updated regularly. The ocsp library has already taken care of this.
- There may be network issues between the server and the CA's OCSP server, resulting in the inability to obtain OCSP information. In this case, the server should degrade to the client's request to the OCSP server. The server should not reject client requests due to the inability to obtain OCSP information.
References#
- Improving TLS Performance by 30%? Talking about OCSP Stapling Practice on Node.JS - A well-written article, the ideas are timeless, but the ocsp library is no longer maintained.
- How to enable ocsp? - I have always wondered if foreign network environments are better, so they don't encounter this problem? There is really little information and no relevant libraries.