Like a bouncer who checks IDs perfectly — unless you write your birthday in Roman numerals
CVE-2026-106069 is a TLS certificate hostname verification bypass in Node.js's tls.checkServerIdentity() function. When a Node.js application connects to a host specified using a non-canonical IP address format — octal (0177.0.0.1), hex (0x7f.0.0.1), shorthand (127.1), decimal integer (2130706433), or fullwidth Unicode digits (127.0.0.1) — the runtime's net.isIP() returns false, treating the host as a domain name. After url.domainToASCII() normalization converts it to the canonical IP 127.0.0.1, the hostname checker compares against DNS SAN entries rather than IP Address SAN entries in the peer certificate. An attacker in a man-in-the-middle position could present a certificate whose DNS SAN matches the normalized IP, impersonating the intended server. All supported release lines are affected: 26.x before 26.12.1, 24.x before 24.22.1, and 22.x before 22.23.4. The fix, committed by Matteo Collina (tls: reject non-canonical IP addresses, commit d7663560ac), rejects any host that is not an IP per net.isIP() but becomes one after normalization, raising ERR_TLS_CERT_ALTNAME_INVALID before matching begins.
The Node.js project rates this HIGH — and from a pure protocol-correctness standpoint, that's defensible: a TLS hostname verification bypass is a fundamental certificate trust violation. But the vendor label doesn't account for the double prerequisite that makes this nearly impossible to trigger in production. First, the attacker needs a man-in-the-middle network position. Second — and this is the killer — the victim Node.js process must connect to the target server using a non-canonical IP string. Production applications don't do this. Standard code uses DNS hostnames or properly formatted IPs; configuration files, environment variables, and orchestration systems all emit canonical addresses. The non-canonical formats (octal, hex, shorthand, fullwidth) are testing curiosities, not production patterns. The vendor HIGH is a reasonable *theoretical* rating, but real-world exploitability is MEDIUM at best.
4 steps from start to impact.
Attacker establishes MITM position
- Network adjacency or upstream routing control relative to the victim Node.js process
- Modern enterprise networks use port security, DHCP snooping, and dynamic ARP inspection that defeat LAN-level MITM
- Cloud-native deployments (K8s, serverless) have minimal L2 broadcast domains
- DNS-over-HTTPS and DNSSEC adoption reduces DNS-based MITM vectors
Victim app connects using non-canonical IP format
0177.0.0.1), hex (0x7f.0.0.1), shorthand (127.1), fullwidth Unicode (127.0.0.1), or bare decimal (2130706433). This causes net.isIP() to return false, triggering the vulnerable code path that treats the host as a DNS name rather than an IP address.- Application code or configuration supplies a non-canonical IP format to a TLS connect call
- The hostname is not pre-validated or normalized before the TLS handshake
- This is the critical bottleneck. Production code, orchestrators, service meshes, and environment variables universally emit canonical IP addresses or DNS names
- Non-canonical IP formats appear only in security research, CTF challenges, or SSRF payloads — not in normal application configurations
- Even user-controlled input that reaches a TLS connect call would typically pass through URL parsing that canonicalizes the IP first
Attacker presents certificate with matching DNS SAN
DNS:127.0.0.1). Because the vulnerable checkServerIdentity() treats the host as a domain name, it looks for DNS SAN matches instead of IP Address SAN matches, and the attacker's certificate passes validation.- Attacker possesses a TLS certificate with a DNS SAN matching the normalized IP address
- The certificate is trusted by the victim's CA trust store (either a public CA cert or the victim trusts the attacker's CA)
- Public CAs are prohibited from issuing certificates with IP addresses in DNS SAN fields per CA/Browser Forum Baseline Requirements
- Only an internal/private CA that doesn't enforce this restriction could issue such a certificate
- Self-signed certs would fail chain validation unless the victim explicitly trusts the attacker's root
Traffic interception / impersonation
- Steps 1-3 completed successfully
- mTLS (mutual TLS) would require the attacker to also authenticate to the server, which they cannot
- Certificate pinning in the application would reject the attacker's cert regardless of SAN matching
- Application-layer encryption or signing (e.g., JWTs, signed payloads) limits the value of the intercepted plaintext
/0[0-7]+\./, /0x[0-9a-f]+\./i, /[0-9]/. If none are found, the vulnerability is not triggerable in your environment. No mitigation SLA — go straight to the 365-day remediation window.- WAF / reverse proxy in front of Node.js — This is a client-side TLS verification issue in outbound connections, not inbound. A WAF protects incoming traffic but has no visibility into how the Node.js process validates outbound TLS connections.
- Network segmentation alone — While it reduces MITM opportunity, it doesn't eliminate it. An attacker already inside the same segment (the prerequisite for this attack) is unaffected by perimeter segmentation.
- Upgrading only the OpenSSL/TLS library — The bug is in Node.js's JavaScript-layer
checkServerIdentity()hostname matching logic, not in the underlying OpenSSL or BoringSSL library. Patching the crypto library alone does not fix this.
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed on CISA KEV. No reports from threat intelligence feeds, GreyNoise, or Shadowserver as of 2026-10-23. |
|---|---|
| Proof-of-Concept | No public PoC. Not indexed on pocindex.io. No GitHub repositories named after this CVE. No ExploitDB or Nuclei templates. The Node.js commit (d7663560ac) includes test cases showing non-canonical IP formats that would trigger the bypass, which could serve as a starting point for reproduction. |
| EPSS Score | Not yet scored. CVE disclosed 2026-10-21; FIRST EPSS API returns no data. Expected to be low given the niche trigger conditions. |
| KEV Status | Not listed on CISA Known Exploited Vulnerabilities catalog. |
| CVSS Vector (estimated) | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N (estimated 7.4 HIGH). AC:H reflects the MITM prerequisite; real-world constraints (non-canonical IP format requirement) push practical severity below what the vector captures. |
| Affected Versions | Node.js 22.x (< 22.23.4), 24.x (< 24.22.1), 26.x (< 26.12.1) — all supported release lines. |
| Fixed Versions | 22.23.4, 24.22.1, 26.12.1 — released 2026-10-21. Distro backports: check nodejs packages in Debian sid/bookworm, Ubuntu noble/jammy, Alpine, RHEL/Fedora for backported fixes. |
| Scanning / Exposure | Not directly scannable from the network — the vulnerability is in client-side TLS validation logic, not in a listening service. Shodan/Censys/FOFA cannot fingerprint this. Detection requires version checks on Node.js installations. |
| Disclosure Date | 2026-10-21 (coordinated disclosure via Node.js security release) |
| Reporter / Credit | Reported by anagnorisis2peripeteia; fixed by Matteo Collina (mcollina), Node.js TSC member. |
Sources.
- Node.js October 2026 Security Releases (official advisory)
- Node.js commit d7663560ac — tls: reject non-canonical IP addresses
- Node.js v24.22.1 release notes
- Lilting.ch — Node.js v24.22.1 fixes four HIGH CVEs
- Astra Security — Node.js October 2026 Security Release analysis
- CyberSecurityNews — Node.js Releases Security Updates
- SecurityOnline — Node.js Fixes 10 Flaws Including TLS Hostname Bypasses
- TuxCare — 10 Node.js CVEs, a Brotli RCE, and What to Do If Your Version Is EOL
Why this verdict
- MITM prerequisite compounds access cost: The attacker must already hold a privileged network position (ARP spoof, DNS poison, BGP hijack, compromised router) before the vulnerability is reachable. This rules out opportunistic remote exploitation and limits the threat to post-initial-access or nation-state-adjacent scenarios.
- Non-canonical IP trigger is vanishingly rare in production: The entire chain depends on the victim Node.js process connecting to a server using an octal, hex, shorthand, or fullwidth-digit IP format. After reviewing the fix's test cases (
0177.0.0.1,0x7f.0.0.1,127.1, fullwidth digits), these formats appear in zero standard frameworks, zero orchestrator outputs, and zero common configuration patterns. This constraint alone drops practical exploitability by at least an order of magnitude. - Certificate acquisition barrier: Even with MITM and a non-canonical IP, the attacker needs a certificate with a DNS SAN matching the normalized IP. Public CAs are prohibited from issuing such certificates by CA/Browser Forum BRs. Only misconfigured internal CAs could produce a matching cert, further narrowing the attack surface.
- Role multiplier — CI/CD and supply-chain roles: Node.js is heavily deployed in CI/CD pipelines, npm tooling, and microservices. However, the chain requires non-canonical IP format in the TLS connection target. CI/CD systems, package managers, and service meshes all use canonical hostnames or IPs. The chain does NOT succeed in these high-value roles under normal deployment. No floor-raising outcome identified.
- Role multiplier — API backends and microservices: Standard Node.js HTTP/HTTPS servers are the *listening* side — this CVE affects the *client* TLS verification side. API backends making outbound TLS connections use SDK-generated URLs with canonical addresses. No floor-raising outcome identified.
- No exploitation evidence, no PoC, no KEV: Two days post-disclosure with zero weaponization signals. The complexity of chaining MITM + non-canonical IP + matching cert makes rapid exploitation unlikely.
Why not higher?
A HIGH or CRITICAL assessment would require the attack chain to succeed in realistic production conditions or high-value deployment roles. The non-canonical IP prerequisite is not merely 'high complexity' — it is a condition that effectively never exists in production Node.js deployments. Standard code, frameworks, orchestrators, and configuration management tools all emit canonical IP addresses. Without evidence of ANY real-world codebase or configuration pattern that feeds non-canonical IPs into TLS connections, elevating beyond MEDIUM would overstate the risk for defenders managing real fleets.
Why not lower?
Despite the narrow trigger, this IS a fundamental TLS hostname verification bypass in a runtime with massive installed base across all three supported LTS/Current lines. If a deployment does happen to use non-canonical IP formats (e.g., legacy configuration, SSRF-adjacent code paths, or applications that pass user-controlled input to TLS connect), the impact is full traffic interception with no user interaction. The theoretical CVSS of 7.4 reflects real confidentiality and integrity impact if triggered. Dropping to LOW would undercount the tail risk for the ~0.1% of deployments that might use unusual IP formats.
Crowdsourced verification payload.
Run this script on each Node.js host (application server, CI runner, container image) to check the installed Node.js version against patched releases. Requires no special privileges — any user who can run node --version is sufficient. Example: chmod +x check_cve_2026_106069.sh && ./check_cve_2026_106069.sh or for a container: docker run --rm <image> node -e "$(cat check_cve_2026_106069.sh)"
#!/usr/bin/env bash
# check_cve_2026_106069.sh
# Checks whether the installed Node.js version is patched against
# CVE-2026-106069 (TLS hostname bypass via non-canonical IP addresses)
# Patched versions: 22.23.4+, 24.22.1+, 26.12.1+
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
if ! command -v node &>/dev/null; then
echo "UNKNOWN — Node.js is not installed or not in PATH"
exit 2
fi
NODE_VERSION=$(node --version 2>/dev/null | sed 's/^v//')
if [[ -z "$NODE_VERSION" ]]; then
echo "UNKNOWN — could not determine Node.js version"
exit 2
fi
# Split version into major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$NODE_VERSION"
# Remove any pre-release suffix from PATCH
PATCH=${PATCH%%-*}
echo "Detected Node.js version: $NODE_VERSION (major=$MAJOR minor=$MINOR patch=$PATCH)"
# Version comparison logic
check_version() {
local maj=$1 min=$2 pat=$3
local fix_maj=$4 fix_min=$5 fix_pat=$6
if (( maj > fix_maj )); then return 0; fi
if (( maj == fix_maj && min > fix_min )); then return 0; fi
if (( maj == fix_maj && min == fix_min && pat >= fix_pat )); then return 0; fi
return 1
}
case $MAJOR in
22)
if check_version "$MAJOR" "$MINOR" "$PATCH" 22 23 4; then
echo "PATCHED — Node.js $NODE_VERSION >= 22.23.4"
exit 0
else
echo "VULNERABLE — Node.js $NODE_VERSION < 22.23.4 (CVE-2026-106069)"
exit 1
fi
;;
24)
if check_version "$MAJOR" "$MINOR" "$PATCH" 24 22 1; then
echo "PATCHED — Node.js $NODE_VERSION >= 24.22.1"
exit 0
else
echo "VULNERABLE — Node.js $NODE_VERSION < 24.22.1 (CVE-2026-106069)"
exit 1
fi
;;
26)
if check_version "$MAJOR" "$MINOR" "$PATCH" 26 12 1; then
echo "PATCHED — Node.js $NODE_VERSION >= 26.12.1"
exit 0
else
echo "VULNERABLE — Node.js $NODE_VERSION < 26.12.1 (CVE-2026-106069)"
exit 1
fi
;;
23|25)
echo "VULNERABLE — Node.js $NODE_VERSION is an EOL odd-numbered release with no security patch available"
exit 1
;;
*)
if (( MAJOR > 26 )); then
echo "PATCHED — Node.js $NODE_VERSION is a newer major version (post-fix)"
exit 0
elif (( MAJOR < 22 )); then
echo "VULNERABLE — Node.js $NODE_VERSION is EOL and will not receive a patch for CVE-2026-106069"
exit 1
else
echo "UNKNOWN — unexpected Node.js major version $MAJOR"
exit 2
fi
;;
esac