← Back to Feed CACHED · 2026-10-21 15:38:54 · CACHE_KEY CVE-2026-105483
CVE-2026-105483 · CWE-295 · Disclosed 2026-10-21

Node.js TLS certificate hostname verification bypass via fullwidth Unicode IP digits

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

It's like an airport that waves you through security because your passport is written in fancy calligraphy

CVE-2026-105483 is a TLS certificate hostname verification bypass in Node.js 24.x (before 24.22.1) and 26.x (before 26.12.1). The flaw exists because net.isIP() does not recognize fullwidth Unicode digits (e.g., 127.0.0.1) as valid IP addresses, but url.domainToASCII() normalizes them into standard ASCII form. When a Node.js TLS client connects to a host expressed in fullwidth digits, the verification logic compared the normalized address against DNS Subject Alternative Name entries in the certificate rather than IP Address SAN entries. A certificate carrying DNS:127.0.0.1 (note: a DNS SAN, not an IP SAN) would incorrectly pass hostname verification for a connection to 127.0.0.1. Node.js 22.x is not affected.

Node.js labels this HIGH, which is reasonable from a pure cryptographic-correctness standpoint — any TLS verification bypass is serious. However, there is no published CVSS score to evaluate. In practice, the severity is significantly lower because the attack requires an application to pass fullwidth Unicode IP addresses into TLS connections, a pattern that is virtually nonexistent in real codebases. The attacker also needs a man-in-the-middle position and a certificate with the target IP in a DNS SAN — a configuration no public CA will issue. This is a defense-in-depth correctness fix, not a field-exploitable bypass in any normal deployment.

"Fullwidth-digit IP bypass in Node.js TLS is clever but nearly impossible to hit in production"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Application accepts fullwidth IP input

The target Node.js application must receive and pass a fullwidth-digit IPv4 address (e.g., 192.168.1.1) as the hostname for an outbound TLS connection. This is an extremely unusual code path — virtually all applications use ASCII IP addresses or DNS hostnames. The input would most likely come from user-controlled data flowing unsanitized into https.request() or tls.connect().
Conditions required:
  • Node.js 24.x < 24.22.1 or 26.x < 26.12.1
  • Application passes user-influenced hostnames to outbound TLS connections
  • Input contains fullwidth Unicode digits, not stripped by any input validation
Where this breaks in practice:
  • Standard HTTP libraries, ORMs, and SDKs never generate fullwidth IP addresses
  • Input validation middleware (express-validator, Joi, Zod) rejects non-ASCII hostnames
  • Even SSRF-prone applications typically receive ASCII URLs from attackers
Detection/coverage: No scanner or DAST tool specifically tests for fullwidth-digit TLS bypass. Static analysis (Semgrep, CodeQL) could be written to detect user input flowing into TLS hostnames without normalization.
STEP 02

Attacker obtains certificate with DNS SAN for the IP

The attacker needs a TLS certificate where the target IP address appears as a DNS SAN entry (e.g., DNS:192.168.1.100), not an IP Address SAN entry. Public certificate authorities will not issue certificates with IP-address-shaped DNS SANs — this effectively limits exploitation to environments trusting self-signed certificates, private CAs with weak issuance policies, or compromised CA infrastructure.
Conditions required:
  • Attacker controls a CA trusted by the client, OR the client trusts self-signed certs
  • Certificate contains IP address as DNS SAN, not IP SAN
Where this breaks in practice:
  • Public CAs (Let's Encrypt, DigiCert, etc.) validate SAN type and reject IP-as-DNS
  • Enterprise PKI with certificate templates typically enforce correct SAN types
  • Applications pinning certificates or using custom CA bundles further narrow the window
Detection/coverage: Certificate transparency logs can be monitored for DNS SANs containing IP-address patterns. Tools like zlint flag this as a lint error.
STEP 03

Attacker achieves MITM position

The attacker must intercept the TLS connection between the Node.js client and the legitimate server. This requires ARP poisoning, DNS hijacking, BGP hijacking, or control of a network hop between the client and server. On internal networks, this is non-trivial where 802.1X or network segmentation is enforced. On the public internet, this requires ISP-level or state-level capabilities.
Conditions required:
  • Attacker has network position to intercept the TLS handshake
  • No certificate pinning in the application
Where this breaks in practice:
  • Corporate networks with NAC, segmentation, and encrypted DNS resist ARP/DNS attacks
  • Cloud-native deployments with service mesh (Istio, Linkerd) use mTLS with pinned certs
  • Internet MITM requires significant attacker capability
Detection/coverage: IDS/IPS (Suricata, Zeek) can detect ARP spoofing and DNS hijacking. Network anomaly detection flags unexpected certificate presentations.
STEP 04

TLS verification bypassed — connection intercepted

With all conditions met, Node.js normalizes the fullwidth IP via domainToASCII(), finds a match in the certificate's DNS SANs, and accepts the connection. The attacker can now read and modify all traffic on this TLS session. Impact is limited to the confidentiality and integrity of this specific connection — there is no code execution, privilege escalation, or lateral movement inherent to the vulnerability itself.
Conditions required:
  • All prior conditions satisfied simultaneously
Where this breaks in practice:
  • The chain of fullwidth input + DNS-SAN cert + MITM position is an extremely narrow conjunction
  • Application-level authentication (API keys, OAuth tokens) inside the TLS session remains a separate barrier
Detection/coverage: Runtime TLS inspection tools or application-level logging of peer certificate details would reveal the SAN type mismatch.
03 · Compensating Control

1
MEDIUM 5.0→IGNORE 0.0
SEVERITY REDUCED
Upgrade Node.js to 24.22.1 or 26.12.1 — The definitive fix. Since this is assessed as MEDIUM, there is no mitigation SLA — go straight to the noisgate remediation SLA of 365 days. Given the minimal risk, schedule this with your next regular Node.js update cycle. If you already track Node.js security releases, this should land in your next monthly rollout.
2
MEDIUM 5.0→IGNORE 0.0
SEVERITY REDUCED
Add input validation rejecting non-ASCII characters in outbound hostnames — If your Node.js applications make outbound HTTPS calls with user-influenced hostnames, add a validation layer that rejects non-ASCII characters before they reach tls.connect() or https.request(). The code if (!/^[\x20-\x7E]+$/.test(hostname)) throw new Error('Invalid hostname') is sufficient. Deploy within your normal development cycle. This breaks the first step of the attack path.
3
MEDIUM 5.0→LOW 2.5
SEVERITY REDUCED
Enforce IP Address SAN type in internal CA certificate templates — If you operate an internal CA (ADCS, Vault PKI, step-ca), ensure certificate templates require IP addresses to use the IP Address SAN type, not DNS SAN. This blocks the attacker from obtaining a certificate that triggers the bypass. Audit existing templates within 30 days.
4
MEDIUM 5.0→IGNORE 0.0
SEVERITY REDUCED
Deploy certificate pinning for critical outbound TLS connections — For high-value outbound connections (e.g., to payment processors, identity providers), implement certificate or public key pinning. This prevents any MITM certificate — regardless of SAN configuration — from being accepted. Use tls.connect({checkServerIdentity}) with a custom verifier.
What doesn't work
  • WAF/NGFW rules — This is a client-side TLS verification bug in outbound connections. Network-layer defenses inspect inbound traffic and cannot see or influence how a Node.js process validates a server certificate.
  • Disabling TLS renegotiation — The vulnerability is in the initial hostname verification during the TLS handshake, not in renegotiation. Renegotiation settings have no effect.
  • Switching to HTTP/2 or HTTP/3 — The bug is in the TLS layer below the HTTP protocol version. QUIC/TLS 1.3 connections using Node.js's built-in TLS stack are equally affected.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not in CISA KEV. No reports of active exploitation campaigns. Disclosed today (2026-10-21) as part of a coordinated Node.js security release.
Proof of ConceptNo public PoC. Not indexed on pocindex.io, GitHub, ExploitDB, or Nuclei templates. The lilting.ch writeup includes a test matrix showing the bypass behavior pre/post-patch but no weaponized exploit.
EPSS ScoreNot yet scored — CVE disclosed 2026-10-21, FIRST EPSS data not yet available. Expected to be very low given the narrow attack surface.
CVSS Vector (noisgate-estimated)CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N → 7.4 base — but real-world exploitability is dramatically lower than the base score suggests due to the fullwidth-digit prerequisite.
Affected VersionsNode.js 24.x (all versions before 24.22.1), Node.js 26.x (all versions before 26.12.1). Node.js 22.x is not affected.
Fixed Versionsv24.22.1 (LTS 'Krypton') and v26.12.1 (Current), both released 2026-10-21. Commit de49c913f2 (v24) / cd4b05469c (v26). Tracked in private PR 24.22.1" target="_blank" rel="noopener">nodejs-private/node-private#985.
Scanning / ExposureNode.js TLS clients are not directly exposed on Shodan/Censys — this is a *client-side* TLS verification bug, not a listening service vulnerability. Exposure depends on whether applications make outbound HTTPS calls with user-influenced hostnames. No GreyNoise tags exist for this CVE.
Disclosure Date2026-10-21 — coordinated release by Node.js security team alongside 6 other CVEs (4 HIGH, 2 MEDIUM, 1 LOW).
Reporter / ResearcherReported by hasanfleyah via HackerOne. Fixed by James M Snell (jasnell), Node.js core team member.
Related CVEsCVE-2026-106069 and CVE-2026-106072 (same release) — reject non-canonical IP addresses in TLS. These are companion fixes hardening the same IP normalization code path.

Sources.

  1. Node.js October 2026 Security Release Advisory
  2. Node.js v24.22.1 Release Notes
  3. Node.js v26.12.1 Release Notes
  4. lilting.ch — Technical Analysis and Testing of CVE-2026-105483
  5. spaceraccoon — Node.js TLS Vulnerability Spoiler Alert
  6. HackerOne Report #3649802 (restricted)
  7. pocindex.io — PoC Exploit Index
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.0/10)

Why this verdict

  • Fullwidth prerequisite is a near-total friction wall: Applications must pass fullwidth Unicode IP addresses (e.g., 127.0.0.1) into TLS hostname parameters. This is not a pattern that exists in any standard Node.js framework, HTTP client library, or package manager. We estimate <0.01% of Node.js deployments would ever exercise this code path in production.
  • MITM + non-standard certificate is a two-gate compound requirement: Beyond the fullwidth input, the attacker needs both a network interception position and a certificate with an IP-as-DNS-SAN — a combination that public CAs will not issue and that internal CAs with proper templates prevent. Each gate independently reduces exploitability by orders of magnitude.
  • No code execution or escalation — impact ceiling is connection interception: Even full exploitation only yields read/modify access to a single TLS session. There is no RCE, no privilege escalation, no persistence mechanism. Application-layer authentication (tokens, API keys) inside the session is a separate barrier.
  • Role multiplier: Node.js occupies high-value roles in CI/CD (npm/yarn/pnpm registries), API gateways, and backend microservices. However, (1) package managers connect to registries via DNS hostnames, never fullwidth IPs — chain does not succeed; (2) API gateways proxying to backends use configured ASCII endpoints — chain does not succeed; (3) even a hypothetical SSRF-adjacent scenario where user input reaches a TLS hostname would require the attacker to inject fullwidth digits, which standard URL parsers reject or normalize. Blast radius in all realistic high-value roles: none. The floor rule does not elevate severity because the chain fails in every high-value deployment pattern.
  • Zero exploitation evidence, zero PoC availability: Disclosed today with no public exploit code, no active campaigns, no KEV listing. The theoretical CVSS base of 7.4 dramatically overstates real-world risk.

Why not higher?

The CVSS base score of 7.4 and the vendor HIGH rating reflect cryptographic correctness — any TLS verification bypass is architecturally serious. However, HIGH or CRITICAL would require a realistic attack path that succeeds in meaningful deployments. The fullwidth-digit prerequisite alone eliminates virtually all real-world attack surface. No attacker tooling, no PoC, no campaigns, and the compound requirement of MITM + non-standard certificate + fullwidth input makes this effectively theoretical.

Why not lower?

This is a genuine TLS hostname verification bypass in an extremely widely-deployed runtime (Node.js powers millions of production services). The cryptographic principle it violates — certificate hostname matching — is a foundational security control. A LOW or IGNORE rating would be inappropriate because: (1) the fix is trivial to apply (version bump), (2) exotic input-handling code does exist in some applications, and (3) the companion CVEs (CVE-2026-106069, CVE-2026-106072) targeting the same code path suggest the attack surface may be broader than this single vector.

06 · Verification

Crowdsourced verification payload.

Run this on each Node.js host (application server, CI runner, container image). Execute as any user: bash check_cve_2026_105483.sh. No elevated privileges required — it only reads the Node.js version string.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_105483.sh — Detect CVE-2026-105483 (Node.js TLS fullwidth IP bypass)
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

NODE_BIN="${1:-node}"

if ! command -v "$NODE_BIN" &>/dev/null; then
  echo "UNKNOWN — '$NODE_BIN' not found in PATH"
  exit 2
fi

VERSION_RAW=$($NODE_BIN --version 2>/dev/null) || { echo "UNKNOWN — failed to get version"; exit 2; }
# Strip leading 'v'
VERSION="${VERSION_RAW#v}"

MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)

# Node.js 22.x and below: NOT AFFECTED
if [ "$MAJOR" -le 22 ]; then
  echo "PATCHED — $VERSION_RAW (v22.x and below are not affected by CVE-2026-105483)"
  exit 0
fi

# Node.js 23.x, 25.x: EOL / non-LTS lines — check if they got a fix
# These lines are not listed as affected; treat as unknown
if [ "$MAJOR" -eq 23 ] || [ "$MAJOR" -eq 25 ]; then
  echo "UNKNOWN — $VERSION_RAW (v${MAJOR}.x not listed in advisory; verify manually)"
  exit 2
fi

# Node.js 24.x: fixed in 24.22.1
if [ "$MAJOR" -eq 24 ]; then
  if [ "$MINOR" -gt 22 ]; then
    echo "PATCHED — $VERSION_RAW (> 24.22.1)"
    exit 0
  elif [ "$MINOR" -eq 22 ] && [ "$PATCH" -ge 1 ]; then
    echo "PATCHED — $VERSION_RAW (>= 24.22.1)"
    exit 0
  else
    echo "VULNERABLE — $VERSION_RAW (CVE-2026-105483: upgrade to >= 24.22.1)"
    exit 1
  fi
fi

# Node.js 26.x: fixed in 26.12.1
if [ "$MAJOR" -eq 26 ]; then
  if [ "$MINOR" -gt 12 ]; then
    echo "PATCHED — $VERSION_RAW (> 26.12.1)"
    exit 0
  elif [ "$MINOR" -eq 12 ] && [ "$PATCH" -ge 1 ]; then
    echo "PATCHED — $VERSION_RAW (>= 26.12.1)"
    exit 0
  else
    echo "VULNERABLE — $VERSION_RAW (CVE-2026-105483: upgrade to >= 26.12.1)"
    exit 1
  fi
fi

# Node.js 27+: assume fixed (post-release)
if [ "$MAJOR" -ge 27 ]; then
  echo "PATCHED — $VERSION_RAW (v27+ post-dates the fix)"
  exit 0
fi

echo "UNKNOWN — $VERSION_RAW (unexpected version)"
exit 2