CVE-2026-43971 Overview
CVE-2026-43971 is an improper output encoding vulnerability [CWE-116] in ninenines/cowlib, the low-level HTTP library used by the Cowboy Erlang web server. The flaw resides in cow_link:do_link/1, which serializes application-supplied values directly into a Link: response header without escaping or token-grammar validation. An attacker who can influence the target URI, rel value, or attribute keys can smuggle additional directives such as rel="preconnect", rel="preload", or rel="prerender". This forces victim browsers to open out-of-band connections to attacker-controlled origins. The issue affects cowlib versions from 2.9.0 onward.
Critical Impact
Attackers can inject arbitrary Link: header directives, coercing victim browsers into contacting attacker-controlled origins via preconnect, preload, and prerender hints.
Affected Products
- ninenines/cowlib from version 2.9.0 onward
- Erlang/OTP web applications using Cowboy that round-trip parsed Link: headers through cow_link:link/1
- Downstream HTTP frameworks that depend on cowlib for header serialization
Discovery Timeline
- 2026-08-18 - CVE-2026-43971 published to NVD
- 2026-08-20 - Last updated in NVD database
Technical Details for CVE-2026-43971
Vulnerability Analysis
The defect lives in cow_link:do_link/1 in src/cow_link.erl. The function interpolates three attacker-influenceable slots — the target URI, the rel value, and attribute keys — directly into the serialized Link: header value. No escaping, quoting, or token-grammar validation is applied before emission.
Because the serializer does not enforce the RFC 8288 grammar, a single unexpected byte breaks out of its intended slot. A > byte in the target closes the URI angle-bracket delimiter early, permitting appended link entries. A " or \ byte in rel terminates the quoted string and opens a new parameter list. Any byte in an attribute key, including whitespace, =, and ", is emitted verbatim.
Browsers act on parsed Link: directives immediately. Injected rel="preconnect", rel="preload", and rel="prerender" values cause victim clients to open TCP, TLS, and HTTP connections to attacker-chosen hosts. This enables tracking, cache pollution, and outbound request smuggling.
Root Cause
The root cause is missing output encoding [CWE-116]. cow_link:do_link/1 concatenates values into the header without validating that each slot conforms to the URI or quoted-string grammar defined in RFC 8288 and RFC 7230.
Attack Vector
An attacker supplies crafted values into any application field that later serializes through cow_link:link/1. Applications that echo parsed Link: headers, expose user-controlled URLs, or accept rel values from untrusted input are exposed. The attack requires no authentication and executes over the network.
// Patch excerpt: include/cow_parse.hrl adds URI character validation
(C =:= $() or (C =:= $)) or (C =:= $*) or (C =:= $+) or
(C =:= $,) or (C =:= $;) or (C =:= $=)).
-define(IS_URI_CHAR(C),
?IS_URI_UNRESERVED(C) or ?IS_URI_GEN_DELIMS(C) or
?IS_URI_SUB_DELIMS(C) or (C =:= $%)).
-define(IS_VCHAR(C), C =:= $\t; C > 31, C < 127).
Source: GitHub Cowlib Commit 89da27e
Detection Methods for CVE-2026-43971
Indicators of Compromise
- Outbound Link: response headers containing > characters in the URI slot or unescaped " and \ bytes inside rel values.
- Server logs showing unexpected rel="preconnect", rel="preload", or rel="prerender" directives originating from user-controlled fields.
- DNS or NetFlow telemetry showing client browsers connecting to unfamiliar origins immediately after page load.
Detection Strategies
- Inspect HTTP responses at the proxy or WAF layer for Link: headers containing multiple angle-bracketed URI entries where only one is expected.
- Audit application code paths that call cow_link:link/1 and identify any that accept untrusted URI, rel, or attribute inputs.
- Compare deployed cowlib version against the fixed release referenced in the CNA advisory.
Monitoring Recommendations
- Enable egress monitoring for browser clients to identify preconnect or prerender traffic to unapproved domains.
- Log full Link: header contents on outbound responses in staging and production for anomaly review.
- Alert on any Erlang release still shipping cowlib2.9.0 through the last unpatched version.
How to Mitigate CVE-2026-43971
Immediate Actions Required
- Upgrade cowlib to the patched release that includes commit 89da27e.
- Audit all invocations of cow_link:link/1 and remove untrusted input from the target, rel, and attribute key slots.
- Sanitize or reject any application input containing >, ", \, =, or whitespace before it reaches Link header construction.
Patch Information
The upstream fix in ninenines/cowlib adds a new IS_URI_CHAR macro in include/cow_parse.hrl and rejects invalid characters when building the Link: header in src/cow_link.erl. Applications should rebuild against the patched dependency and redeploy. Refer to the OSV entry EEF-CVE-2026-43971 for ecosystem tracking.
Workarounds
- Strip or URL-encode >, ", and \ from any user-supplied value before passing it to cow_link:link/1.
- Restrict rel values to a server-side allowlist such as next, prev, or canonical.
- Disable pass-through of parsed Link: headers from upstream services until the library is upgraded.
# Example rebar3 dependency pin after upgrading cowlib
{deps, [
{cowboy, "~> 2.x"},
{cowlib, {git, "https://github.com/ninenines/cowlib", {ref, "89da27ee4c241f5d649ba7d9b7f2188918af6cea"}}}
]}.
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

