tls: reject chains containing blocked public keys · nodejs/node@01acf0e · GitHub
Skip to content

Commit 01acf0e

Browse files
UlisesGasconaduh95
authored andcommitted
tls: reject chains containing blocked public keys
Node.js embeds the Mozilla root store but has no equivalent of the browsers' CRLSet/OneCRL and no default online revocation checking, so a certificate chain through a compromised intermediate still verifies as long as it reaches a trusted root. Removing the affected roots would over-block, since other legitimate intermediates chain to them. Add a small compiled-in blocklist of SubjectPublicKeyInfo SHA-256 hashes and check every verified chain against it from a single SSL_CTX_set_cert_verify_callback hook. A hit is reported as X509_V_ERR_CERT_REVOKED through the normal verification path, so it honours rejectUnauthorized and surfaces as authorizationError on both client and server sockets. The hook is installed for every SSL_CTX Node.js creates: SecureContext, QUIC and DTLS. The OpenSSL 4.1 CN name constraint handling previously registered from crypto_tls.cc moves into the same hook, since OpenSSL allows only one. The initial entries are the eight eMudhra/emSign intermediates whose private keys were reported compromised in October 2026. The same list is shipped by Go (CVE-2026-111943) and Electron. Sessions resumed from before this change are not re-verified, as is standard for TLS resumption; the exposure ends with the session timeout. Test-only hooks on the crypto binding, reachable with --expose-internals, allow the test to block a fixture key and exercise the full handshake. Refs: electron/electron#55126 Refs: golang/go#82345 Assisted-by: Claude, Codex Signed-off-by: Ulises Gascon <ulisesgascongonzalez@gmail.com> PR-URL: #66871 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Bryan English <bryan@bryanenglish.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent 1e39cd5 commit 01acf0e

5 files changed

Lines changed: 538 additions & 0 deletions

File tree

‎src/crypto/crypto_context.cc‎

Lines changed: 258 additions & 0 deletions

‎src/crypto/crypto_context.h‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ X509_STORE* NewRootCertStore(Environment* env);
2727

2828
X509_STORE* GetOrCreateRootCertStore(Environment* env);
2929

30+
// Installs the certificate verification callback that applies the SPKI
31+
// blocklist (and, on OpenSSL 4.1+, the CN name-constraint handling) to every
32+
// chain verified through `ctx`. Must be called for every SSL_CTX Node.js
33+
// creates, since SSL_CTX_set_cert_verify_callback replaces any earlier hook.
34+
void InstallCertVerifyCallback(SSL_CTX* ctx);
35+
3036
ncrypto::BIOPointer LoadBIO(Environment* env, v8::Local<v8::Value> v);
3137

3238
class SecureContext final : public BaseObject {
@@ -160,6 +166,15 @@ class SecureContext final : public BaseObject {
160166
template <bool primary>
161167
static void GetCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
162168

169+
// Test-only hooks for the SPKI blocklist that every SSL_CTX applies
170+
// during certificate verification. Reachable through
171+
// internalBinding('crypto') with --expose-internals. The argument is the
172+
// SHA-256 of a DER-encoded SubjectPublicKeyInfo.
173+
static void AddBlockedSPKIForTesting(
174+
const v8::FunctionCallbackInfo<v8::Value>& args);
175+
static void ClearBlockedSPKIsForTesting(
176+
const v8::FunctionCallbackInfo<v8::Value>& args);
177+
163178
static int TicketKeyCallback(SSL* ssl,
164179
unsigned char* name,
165180
unsigned char* iv,

‎src/crypto/crypto_tls.cc‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ int TLSExtStatusCallback(SSL* s, void* arg) {
350350
void ConfigureSecureContext(SecureContext* sc) {
351351
// OCSP stapling
352352
sc->ctx().setStatusCallback(TLSExtStatusCallback);
353+
// The cert-verify callback (SPKI blocklist and the OpenSSL 4.1 CN name
354+
// constraint handling) is installed once per SSL_CTX in
355+
// SecureContext::Init, so that it also covers contexts created without a
356+
// TLSWrap.
353357
}
354358

355359
inline bool Set(

‎src/quic/tlscontext.cc‎

Lines changed: 6 additions & 0 deletions

0 commit comments

Comments
 (0)