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.
4 steps from start to impact.
Application accepts fullwidth IP input
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().- 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
- 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
Attacker obtains certificate with DNS SAN for the IP
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.- 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
- 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
zlint flag this as a lint error.Attacker achieves MITM position
- Attacker has network position to intercept the TLS handshake
- No certificate pinning in the application
- 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
TLS verification bypassed — connection intercepted
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.- All prior conditions satisfied simultaneously
- 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
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.tls.connect({checkServerIdentity}) with a custom verifier.- 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.
The supporting signals.
| In-the-Wild Exploitation | None 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 Concept | No 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 Score | Not 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 Versions | Node.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 Versions | v24.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 / Exposure | Node.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 Date | 2026-10-21 — coordinated release by Node.js security team alongside 6 other CVEs (4 HIGH, 2 MEDIUM, 1 LOW). |
| Reporter / Researcher | Reported by hasanfleyah via HackerOne. Fixed by James M Snell (jasnell), Node.js core team member. |
| Related CVEs | CVE-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.
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.
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.
#!/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