← Back to Feed CACHED · 2026-10-23 04:10:29 · CACHE_KEY CVE-2026-106069
CVE-2026-106069 · CWE-295 · Disclosed 2026-10-21

Node.js TLS certificate hostname verification bypass via non-canonical IP address normalization

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

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.

"TLS hostname bypass via non-canonical IPs is theoretically nasty but practically a ghost hunt"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attacker establishes MITM position

The attacker must position themselves between the victim Node.js client process and the target server. This requires ARP spoofing on the same LAN, DNS poisoning, BGP hijacking, or control of an intermediate network device. Without this network position, no part of the exploit chain proceeds.
Conditions required:
  • Network adjacency or upstream routing control relative to the victim Node.js process
Where this breaks in practice:
  • 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
Detection/coverage: Network IDS signatures for ARP spoofing; cloud VPC flow logs for anomalous routing
STEP 02

Victim app connects using non-canonical IP format

The Node.js application must construct a TLS connection to a host specified in a non-canonical IP format — octal (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.
Conditions required:
  • 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
Where this breaks in practice:
  • 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
Detection/coverage: Code audit / grep for hardcoded non-canonical IPs; runtime TLS connection logging
STEP 03

Attacker presents certificate with matching DNS SAN

With MITM established and the victim connecting via non-canonical IP, the attacker intercepts the TLS handshake and presents a certificate containing a DNS SAN entry that matches the *normalized* IP (e.g., 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.
Conditions required:
  • 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)
Where this breaks in practice:
  • 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
Detection/coverage: Certificate Transparency log monitoring; TLS inspection proxies would flag unusual DNS SAN entries
STEP 04

Traffic interception / impersonation

With the TLS handshake completed using the attacker's certificate, the attacker can now read and modify traffic between the victim Node.js process and what it believes is the legitimate server. Depending on the application, this could expose API keys, authentication tokens, or application data in transit.
Conditions required:
  • Steps 1-3 completed successfully
Where this breaks in practice:
  • 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
Detection/coverage: Application-level integrity checks; anomalous certificate fingerprint alerting
03 · Compensating Control

1
MEDIUM 5.3→IGNORE 0.0
SEVERITY REDUCED
Upgrade Node.js to 22.23.4, 24.22.1, or 26.12.1 — The definitive fix. These versions reject non-canonical IP addresses before certificate matching. Since this is a MEDIUM verdict, deploy within the noisgate remediation SLA of 365 days, though given the minimal upgrade friction for a point release, most teams should roll this into regular patch cycles within 30-60 days.
2
MEDIUM 5.3→IGNORE 0.0
SEVERITY REDUCED
Audit codebase for non-canonical IP address usage in TLS connections — Search your Node.js codebases for hardcoded or dynamically constructed IP addresses in non-canonical formats (octal, hex, shorthand, fullwidth). Grep patterns: /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.
3
MEDIUM 5.3→LOW 2.5
SEVERITY REDUCED
Enforce certificate pinning or SPKI pinning for critical outbound TLS connections — For high-value outbound connections (e.g., to payment processors, identity providers, package registries), implement certificate or public key pinning. This defeats the attack even if the hostname check is bypassed, because the attacker's certificate will have the wrong public key.
4
MEDIUM 5.3→LOW 2.0
SEVERITY REDUCED
Deploy mTLS for internal service-to-service communication — Mutual TLS requires both sides to authenticate. Even if the attacker's cert passes hostname verification on the client side, they cannot authenticate to the real server. Service meshes like Istio, Linkerd, and Consul Connect provide this automatically.
What doesn't work
  • 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.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not listed on CISA KEV. No reports from threat intelligence feeds, GreyNoise, or Shadowserver as of 2026-10-23.
Proof-of-ConceptNo 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 ScoreNot yet scored. CVE disclosed 2026-10-21; FIRST EPSS API returns no data. Expected to be low given the niche trigger conditions.
KEV StatusNot 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 VersionsNode.js 22.x (< 22.23.4), 24.x (< 24.22.1), 26.x (< 26.12.1) — all supported release lines.
Fixed Versions22.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 / ExposureNot 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 Date2026-10-21 (coordinated disclosure via Node.js security release)
Reporter / CreditReported by anagnorisis2peripeteia; fixed by Matteo Collina (mcollina), Node.js TSC member.

Sources.

  1. Node.js October 2026 Security Releases (official advisory)
  2. Node.js commit d7663560ac — tls: reject non-canonical IP addresses
  3. Node.js v24.22.1 release notes
  4. Lilting.ch — Node.js v24.22.1 fixes four HIGH CVEs
  5. Astra Security — Node.js October 2026 Security Release analysis
  6. CyberSecurityNews — Node.js Releases Security Updates
  7. SecurityOnline — Node.js Fixes 10 Flaws Including TLS Hostname Bypasses
  8. TuxCare — 10 Node.js CVEs, a Brotli RCE, and What to Do If Your Version Is EOL
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.3/10)

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.

06 · Verification

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)"

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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