1. Introduction
Developers wish to have control over the resources loaded into their pages' contexts and the endpoints to which their pages can make requests. This control is necessary for several purposes, including limiting the ways in which users' data can flow through the user agent (mitigating exfiltration attacks) and ensuring control over a site’s architecture and dependencies.
Content Security Policy addresses some of this need, but does so in a way that is more granular than necessary for the most critical use cases, and with a syntax and grammar that’s complicated by the other protections CSP is used to deploy. [CSP]
`Connection-Allowlist` steps back from CSP, and focuses on the single use case of controlling
the explicit requests a page may initiate through Fetch and other web platform APIs (WebRTC, Web
Transport, FedCM, Web Payments, DNS Prefetch, etc) in a way that aims to be straightforward and
comprehensive.
NOTE: '\' line wrapping per RFC 8792
Connection-Allowlist: (response-origin "https://cdn.example" "https://*.example.:tld" \
"https://api.example:*"); report-to=ReportingAPIEndpoint
This header, delivered along with a document to which a user has navigated, would restrict that
document to allow requests and connections only to those endpoints which matched the URL patterns
[URLPATTERN] specified in the list: the origin from which the document was delivered,
https://cdn.example, any subdomain of any host whose penultimate DNS label is example,
https://api.example on any port, and so on.
Attempts to connect to endpoints that don’t match the allowlist will be blocked, and reported via
a Reporting API [REPORTING] endpoint specified in the report-to parameter (and defined
through a separate Reporting-Endpoints header).
1.1. Threat Model
This proposal is intentionally small, targeting a specific but useful niche of client-side attacks and/or misconfigurations:
-
Policies for documents and workers will be asserted by servers as HTTP response headers. This means that attackers who can manipulate response headers will remain out of scope.
-
A document’s (or worker’s) asserted policy governs only requests initiated by _that_ context. If a framed document asserts a distinct policy, so be it (with the caveat that this policy will apply to contexts created via local schemes (
data:,about:, etc.), similar to other components of a context’s policy container, which are handled by HTML when creating new document/worker contexts. -
The connection and/or request are the threat the proposal aims to defend against. To be effective as an exfiltration defense, we must block connections before they’re made.
-
There are a plethora of side-channels available as unintended effects of otherwise-excellent web platform APIs. This proposal does not attempt to address them, focusing instead solely on those requests or connections explicitly initiated by the user agent on a page’s behalf. This certainly includes the clear cases of
fetch()andXMLHttpRequest, along with resource requests generally. It also includes network connections established through channels that are less explicitly "requests": manifests fetched through the Web Install API, connections to TURN/STUN servers via WebRTC, Web Transport channels, DNS prefetch, navigations, and so on. These are all in scope, while more esoteric channels like memory or CPU consumption, socket exhaustion, and XSLeaks in general are not. -
This proposal addresses only communication channels. It does not aim to prevent (or even substantially mitigate) threats like content injection or cross-site scripting. It only can constrain the impact of such an attack _on those specific pages_ where the policy is in place, and should be considered only as one layer in a page’s defenses; it won’t be sufficient in itself.
-
It’s tempting to attempt to defend against a subset of server-side threats, like open redirects. We’ll struggle to do so in an effective manner, given that data which has left the client is fundamentally now under the server’s control. That said, it seems reasonable to allow developers to choose whether or not the client will directly cooperate with servers' decisions to create connections to other servers through redirect responses. We’ll provide two options, with some room for potential extension in the future if more granularity is required: by default, redirect responses regardless of destination will be blocked. Developers can choose to allow all redirect responses by setting a parameter on the header. See § 6.6 Redirects for details.
-
Similarly, WebRTC connections are difficult to constrain via URL patterns, as they often involve dynamic endpoint discovery and peer-to-peer connections. We’ll provide a global toggle to allow developers to choose whether to allow or block WebRTC connections entirely. See § 6.7 WebRTC for details.
1.2. Overlap with Content Security Policy
This proposal has a lot in common with Content Security Policy’s approach to restrictions upon resource usage within a given context, fetch directives in particular. Still, it seems reasonable to explore for a few reasons:
-
CSP’s model is too granular: Developers who wish to mitigate the risk that data flows out of a sensitive context require a protection that exhaustively covers the possible ways in which requests can be made or connections established. CSP’s categorization of requests into types which can be controlled in isolation is the wrong way to approach this problem, as data leaking through a request for a web font is just as bad as data leaking through a request for an image or a script. Distinguishing these request types complicates the process of designing a reasonable defense with questions that are simply irrelevant.
-
CSP’s syntax is not granular enough: The
host-sourcegrammar CSP supports leads to truly verbose headers being delivered with responses. A distinct policy provides the opportunity to shift to the URLPattern syntax which will resolve some complaints folks have raised about CSP’s approach by providing a more modern, malleable, and standardized matching syntax. -
CSP’s coverage is incomplete: While CSP does a good job covering HTTP requests which run through Fetch, it does not exhaustively cover the myriad ways in which web platform APIs allow connections to be established. DNS prefetch and WebRTC are good examples to start with, but there are many others which have struggled with exactly how they fit into CSP’s threat model. By creating a new policy with a narrow focus and explicit promise to developers, these discussions will have a defensible answer and a clear mandate to specification authors.
2. Connection Allowlists
A Connection Allowlist represents the set of URL patterns to which a given context is allowed to connect. It is a struct with the following items:
-
allowlist, which is a list of URL patterns. It is an empty list unless otherwise specified.
-
reporting endpoint, which is either
nullor a Reporting API endpoint. It isnullunless otherwise specified. -
disposition, which is either enforce or report.
-
redirects, which is either allow or block. It is block unless otherwise specified.
-
webrtc, which is either allow or block. It is block unless otherwise specified.
3. Connection Allowlist Headers
The Connection-Allowlist response header contains a list of serialized URL pattern strings that define the set of endpoints to which a context is allowed to connect. This allowlist is enforced for a given context, blocking outgoing connections that don’t match the asserted patterns. The Connection-Allowlist-Report-Only response header is a report-only variant, parsed in the same way, but only sending violation reports without blocking outgoing connections.
These Connection Allowlist headers are structured headers whose values are a list of inner lists. Servers may deliver a list with an arbitrary number of items, but only the first will be used. Any additional items in the list will be ignored.
The inner list can contain either URL Patterns serialized as
strings, or the token
response-origin which represents a pattern matching
the response’s URL’s origin. Unexpected values will be ignored.
The inner list may have arbitrary parameters:
-
The
report-toparameter’s value will be parsed as a token representing a Reporting API endpoint [REPORTING]. -
The
redirectsparameter’s value will be parsed as a token. If it is present, it will be used to set the allowlist’s redirects. It will be set to block if the value is the tokenblock, and allow otherwise. -
The
webrtcparameter’s value will be parsed as a token. If it is present, it will be used to set the allowlist’s webrtc. It will be set to block if the value is the tokenblock, and allow otherwise.
All other parameters will be ignored.
3.1. Parsing
-
Let allowlists be an empty list.
-
Let header be the result of getting a structured field value named `
Connection-Allowlist` as a list from response’s header list. -
Parse a Connection Allowlist header given header, response’s URL, and enforce. If the result is not
null, insert it into allowlists. -
Let header be the result of getting a structured field value named `
Connection-Allowlist-Report-Only` as a list from response’s header list. -
Parse a Connection Allowlist header given header, response’s URL, and report. If the result is not
null, insert it into allowlists. -
Return allowlists.
-
If list’s size is 0, return
null. -
If list[0] is not an inner list, return
null. -
Let allowlist be a Connection Allowlist whose disposition is disposition.
-
For each item in list[0]:
-
Let serialized pattern be
null. -
If item is the token
response-origin:-
Set serialized pattern to the ASCII serialization of response-url’s origin.
-
-
If item is a string, set serialized pattern to item.
-
If serialized pattern is
null, continue. -
Let URL pattern be the result of executing build a URL pattern from an HTTP structured field value given serialized pattern with
nullas the base URL.If this step throws an error, continue.
-
-
For each key → value in list[0]'s parameters:
-
If key is
report-toand value is a token, set allowlist’s reporting endpoint to value.
-
-
Return allowlist.
Note: We’re skipping over any invalid input in the parsing algorithm. We could plausibly be more draconian in our parsing, but that would likely limit our future flexibility.
3.2. Matching
Depending on the type of connection being established, we may have a request to work with, we
may only have a URL, or we may have even less. dns-prefetch,
for example, can only match against a host. The algorithms below spell out how connection allowlist
checks work in these scenarios:
-
For each pattern in connection allowlist’s allowlist:
-
Let input be a new
URLPatternInitdictionary whosehostnameis set to pattern’s hostname component. -
Let host-only pattern be the result of creating a URL pattern given input,
nullas the base URL, and an empty map as the options. -
Let synthetic url be the result of parsing the concatenation of "https://" and host as a URL.
-
If URL pattern matching given host-only pattern and synthetic url does not return
null, return success.
-
-
Return failure.
Note: By creating a new pattern with only the hostname component and synthesizing a URL for host, we’re able to return a match if _any_ pattern in the allowlist could allow a request to that host using any protocol, on any port, with any path, and so on.
-
For each connection allowlist in connection allowlists:
-
Report a violation given url, environment, and connection allowlist.
-
If connection allowlist’s disposition is enforce, return blocked.
-
Return allowed.
-
Let allowlists be request’s policy container’s connection allowlists.
-
For each allowlist in allowlists:
-
If request’s URL list’s size is greater than 1:
-
Report a violation given request’s url, request’s client, and allowlist.
Note: When redirects are blocked, we’re intentionally reporting request’s url and not its current url to avoid leaking more information than necessary about redirect targets.
-
If allowlist’s disposition is enforce, return blocked.
-
Report a violation given request’s url, request’s client, and allowlist.
-
If allowlist’s disposition is enforce, return blocked.
-
-
Return allowed.
-
For each connection allowlist in connection allowlists:
-
If host host-matches connection allowlist, continue.
-
Report a violation given host, environment, and connection allowlist.
-
If connection allowlist’s disposition is enforce, return blocked.
-
-
Return allowed.
-
Let allowlists be environment’s policy container’s connection allowlists.
-
For each allowlist in allowlists:
-
Report a violation given "
webrtc", environment, and allowlist. -
If allowlist’s disposition is enforce, return blocked.
-
Return allowed.
3.3. Reporting
Like other policy mechanisms, Connection Allowlists will report each violation to a Reporting API endpoint specified in the allowlist headers. Violations are represented by the following dictionary type:
enum {ConnectionAllowlistDisposition ,"enforce" };"report" dictionary :ConnectionAllowlistViolationReport ReportBody {USVString ;url USVString ;connection sequence <DOMString >;allowlist ConnectionAllowlistDisposition ; };disposition
ConnectionAllowlistViolationReport’s connection is the
serialized URL of the connection which violated the allowlist.
ConnectionAllowlistViolationReport’s allowlist is the
allowlist which was violated.
ConnectionAllowlistViolationReport’s disposition
is the allowlist’s disposition.
webrtc" (resource URL), an environment (environment), and a
connection allowlist (allowlist):
-
If allowlist’s reporting endpoint is
null, return. -
Let violation be a new
ConnectionAllowlistViolationReport, initialized as follows:
url-
environment’s creation URL, stripped for use in reports.
connection-
If resource URL is a URL, then resource URL, stripped for use in reports.
Otherwise, resource URL.
allowlist-
A new list containing the result of serializing each pattern in allowlist’s allowlist
disposition-
allowlist’s disposition.
-
Generate and queue a report given environment as the context, "
connection-allowlist" as the type, allowlist’s reporting endpoint as the destination, and violation as the data.
4. Embedded Enforcement
Documents often embed content delivered by other servers, and wish to ensure that the embedded
content is at least as constrained as they require. The `Connection-Allowlist` header lets a
server constrain its own connections, but gives an embedder no control over the
connection allowlists applied to the documents it frames. This section defines an opt-in
mechanism, modeled on [csp-embedded-enforcement], that allows an embedder to require a particular
connection allowlist of a framed document.
An embedder declares the connection allowlist it requires of a frame via the
connectionAllowlist content attribute. The user agent communicates the
requirement to the framed document in the `Sec-Required-Connection-Allowlist` request header.
The framed document opts into the requirement either by asserting a `Connection-Allowlist` which
satisfies the requirement, or by returning an `Allow-Connection-Allowlist-From`
response header naming the embedder’s origin. Documents delivered from local schemes
(such as about:srcdoc and data:) opt in implicitly, as they inherit their embedder’s
policy container. A frame that does not opt in is blocked.
When a frame opts in, the required connection allowlist is added to the framed Document’s policy container alongside any allowlist the document asserts for itself, and the requirement is inherited by the frame’s descendants via the required connection allowlist.
https://good.example and the widget’s own
origin:
< iframe connectionAllowlist = '("https://good.example" response-origin)' src = "https://widget.example/" ></ iframe >
The user agent sends the requirement along with the navigation request:
GET / HTTP / 1.1 Host : widget.example Sec-Required-Connection-Allowlist : ("https://good.example" response-origin)
The widget opts in either by acknowledging the embedder’s origin...
Allow-Connection-Allowlist-From: https://embedder.example
...or by asserting a connection allowlist which satisfies the requirement:
Connection-Allowlist: ("https://good.example" response-origin)
In either case the frame loads, and the resulting Document is constrained to
https://good.example and its own origin. Had the widget returned neither header (and not
been delivered from a local scheme), the frame would have been blocked.
4.1. The connectionAllowlist attribute
partial interface HTMLIFrameElement { [CEReactions ]attribute DOMString connectionAllowlist ; };
The connectionAllowlist content attribute gives the
connection allowlist which the embedder requires of the Document the iframe frames. Its
value uses the same grammar as the `Connection-Allowlist` header: a structured header
list of inner lists (see § 3 Connection Allowlist Headers).
The connectionAllowlist IDL attribute must reflect the
connectionAllowlist content attribute.
Note: Unlike a server-asserted `Connection-Allowlist`, which a document applies to itself, the
connectionAllowlist attribute is set by an embedder and applied to another document. To
avoid letting an embedder silently impose a policy upon unwilling cross-origin content, the framed
document MUST opt in; see § 4.4 Obtaining and enforcing a requirement.
4.2. Embedded Enforcement Headers
The Sec-Required-Connection-Allowlist request header communicates
the connection allowlist an embedder requires of a framed document. Its value is a
structured header list using the `Connection-Allowlist` grammar. Because
its name is Sec--prefixed, it is a forbidden request-header, so it is set only by the user agent
and cannot be set or modified by script.
The Allow-Connection-Allowlist-From response header allows a
document to opt into having an embedder enforce a required connection allowlist upon it. It shares
the grammar and comparison of Allow-CSP-From,
matching the embedder’s origin exactly as Fetch matches Access-Control-Allow-Origin:
Allow-Connection-Allowlist-From = [=origin-or-null=] / [=wildcard=]
-
Let origin be the result of getting `
Allow-Connection-Allowlist-From` from response’s header list. -
If origin is
`*`, return true. -
Return true if the result of byte-serializing a request origin with request is origin; otherwise return false.
Note: This mirrors Allow-CSP-From and the CORS check: the header is compared byte-for-byte against the embedder’s serialized request origin (or the wildcard); it is not parsed at all (neither as a URL nor as an origin).
4.3. Comparing allowlists
Equality for structs is not yet well-defined. Two URL patterns are equal here when they were created from the same serialized string. (See also whatwg/infra#710.) [whatwg/infra Issue #664]
Note: This is a (non-strict) subset relationship: every URL pattern in a has to appear in b, but a and b can be equal. A response asserting exactly the requirement therefore satisfies it.
Note: The comparison above requires only that the response’s asserted URL patterns are present in the requirement, without any semantic analysis of those patterns to determine whether they’re logically subsumed. This is intentional: deciding whether one URL pattern fully encompasses another is a difficult problem given their syntax, and conservative set-membership is sufficient for developers' use cases today.
Note: Because the allowlists being compared are resolved against the same URL, the
response-origin token resolves identically in each.
-
candidate’s allowlist is a subset of requirement’s allowlist.
-
If requirement’s redirects is block, then candidate’s redirects is block.
-
If requirement’s webrtc is block, then candidate’s webrtc is block.
Note: An empty candidate allowlist permits no endpoints, and (being a subset of every allowlist) therefore satisfies any requirement whose redirect and WebRTC dispositions it also meets.
Note: "satisfies" captures the "at least as strict as" relationship used throughout this section: the allowlist comparison (is a subset of) handles the URL patterns, while the redirects and webrtc dispositions are handled as separate conditions.
4.4. Obtaining and enforcing a requirement
We add a required connection allowlist item to the
policy container struct, which is either null or a string (a serialized requirement
expressed in the `Connection-Allowlist` header grammar), and is initially null. It represents the
requirement that a context imposes upon the documents (and workers) it embeds. The requirement is
inherited by local-scheme documents and workers directly as the policy container is copied into
those contexts, so a constrained context cannot escape its constraints by embedding a more permissive
child.
Note: The requirement is stored as a serialized string, rather than as a resolved connection allowlist, so
that the response-origin token resolves against each framed
response’s URL independently as the requirement is inherited down the frame tree.
Resolving it once (at the embedder) would freeze response-origin to the embedder’s origin and
mis-apply it to descendants. This mirrors [csp-embedded-enforcement], which likewise stores its
requirement on the policy container in serialized form and parses it only when applying it.
Note: Storing the requirement on the policy container (rather than on the Document) follows [csp-embedded-enforcement]’s treatment of its analogous policy-container item, and is what lets the same mechanism constrain workers.
-
Let container be navigable’s container.
-
Let parent required be container’s node document’s policy container’s required connection allowlist.
-
If container is an
iframeelement that has aconnectionAllowlistcontent attribute whose value (attribute value) is not the empty string, and attribute value is a valid connectionAllowlist attribute value given parent required, then return attribute value. -
Return parent required.
A string (value) is a valid connectionAllowlist attribute value given a requirement parent required if all of the following are true:
-
value is not the empty string.
-
value’s length is less than or equal to 4096.
Note: The 4096 limit is an arbitrary bound that keeps the serialized requirement small enough to carry in the `
Sec-Required-Connection-Allowlist` request header and prevents it from growing without limit as it is inherited down the frame tree. The exact value is not significant. -
value can be parsed as a structured header list whose first item is an inner list.
-
parent required is
null, or value is at least as strict as parent required.
Note: A frame can tighten, but never loosen, the requirement inherited from its parent: a
connectionAllowlist attribute is honored only when it is at least as strict as the parent’s
requirement; otherwise the parent’s requirement is inherited unchanged. This mirrors
[csp-embedded-enforcement]’s validity requirement that an iframe csp attribute be subsumed by
the embedder’s own requirement.
response-origin token is treated as a
literal URL pattern built from the string "response-origin" rather than being resolved against a
URL.
Note: This comparison is performed at navigation time, without resolving
response-origin against any URL. Because the
embedder’s requirement and the frame’s own requirement are each later resolved against the same
per-frame response’s URL, treating the token as equal to itself here is sound, and it
avoids depending on a URL that a cross-origin redirect could change. The comparison is
conservative: a frame whose attribute is not syntactically at least as strict as its parent’s
requirement simply inherits that requirement unchanged.
-
Let list be the result of parsing value as a structured header list.
-
If list is failure, return
null. -
Return the result of parsing a Connection Allowlist header given list, response-url, and disposition.
null (requirement), return
allowed or blocked:
-
If requirement is
null, return allowed. -
Let required be the result of parsing a serialized connection allowlist requirement given requirement, response’s URL, and enforce.
-
If required is
null, return allowed.Note: A malformed requirement is ignored rather than blocking the frame.
-
If response allows a connection allowlist from request, return allowed.
-
Let header be the result of getting a structured field value named `
Connection-Allowlist` as a "list" from response’s header list. -
Let asserted be the result of parsing a Connection Allowlist header given header, response’s URL, and enforce.
-
If asserted is not
nulland asserted satisfies required, return allowed.Note: A malformed asserted `
Connection-Allowlist` parses tonulland is therefore not treated as an opt-in. -
Return blocked.
Note: Local scheme documents (such as about:srcdoc and data:) have no
response and do not pass through this check; they inherit their embedder’s policy container
(including the requirement it stores) directly. See § 5.2 Integration with HTML.
5. Monkey-Patches
5.1. Integration with Fetch
We’ll handle requests by adding a blocking check in Fetch § 4.1 Main fetch alongside other checks that serve the same purpose:
-
If should request be blocked due to a bad port, should fetching request be blocked as mixed content, should request be blocked by Content Security Policy, should request be blocked by Connection Allowlists, or should request be blocked by Integrity Policy Policy returns blocked, then set response to a network error.
Fetch also defines algorithms at a lower level which are used to establish connections for APIs which aren’t based on requests. We’ll hook into resolve an origin and obtain a connection to handle things like DNS prefetch, Web Transport, etc:
We’ll call out to the host-only matching algorithm above to determine whether any pattern could potentially allow a connection to a given host. If not, we’ll fail resolution.
-
If environment is not null:
-
Let allowlists be environment’s policy container’s connection allowlists.
-
If should host be blocked by Connection Allowlists returns blocked when executed upon origin’s host, environment, and allowlists, then return failure.
-
Document environment (defaulting to null):
We’ll add a check before the current step 2:
-
If environment is not null:
-
Let allowlists be environment’s policy container’s connection allowlists.
-
If should url be blocked by Connection Allowlists returns blocked when executed upon url, environment, and allowlists, then return failure.
-
The changes to Fetch will require us to pass additional information into low-level algorithms' callsites to identify the allowlist which ought to be used and the context to be used for reporting. It might be better to instead ask those callsites to perform the checks themselves. My feeling is that we’ll be more successful by centralizing the logic, but it might be simpler to take a piecemeal approach.
5.2. Integration with HTML
To integrate the above into HTML, we’ll add a new connection allowlists item to the policy container struct, containing a list of connection allowlists. This will be populated by adding a step to the create a policy container from a fetch response algorithm:
-
Parse Integrity-Policy headers with response and result.
- Set result’s connection allowlists to the result of parsing a response’s Connection Allowlists given response.
-
Return result.
Note: Early hint integration does not need further changes, as the early response’s policy container is already used when fetching early hint links.
To support § 4 Embedded Enforcement, we follow the model that [csp-embedded-enforcement] uses for its required CSP, threading the embedder’s requirement from the embedding element through target snapshot params and navigation params to the framed Document’s policy container. Concretely, HTML and Fetch are patched as follows:
-
A required connection allowlist item is added to the policy container struct (defined in § 4.4 Obtaining and enforcing a requirement).
-
A required connection allowlist item, either
nullor a string, is added to the target snapshot params struct. HTML’s snapshotting target snapshot params algorithm sets it to the result of determine the required connection allowlist given the target navigable. -
A matching item is added to the navigation params struct, and navigate, create navigation params by fetching, and create navigation params from a srcdoc resource set it from the target snapshot params item.
-
create navigation params by fetching, after creating the request, sets the `
Sec-Required-Connection-Allowlist` header in the request’s header list to the target snapshot params item, when that item is notnull. -
create a policy container from a fetch response takes the requirement as an additional argument, and after populating result’s connection allowlists runs the apply a required connection allowlist steps below.
Note: Like the sandboxing flags already carried in target snapshot params, the requirement is
snapshotted from the iframe container at navigation time, on every
navigation of the navigable, including those the framed Document initiates itself. The
constraint therefore binds subsequent same-navigable navigations and cannot be escaped by the
framed content navigating itself elsewhere. A change to the connectionAllowlist content attribute
takes effect on the next navigation of the navigable, mirroring how the sandbox attribute
behaves. (Note that this differs from referrerpolicy, which only influences navigations the
container itself initiates.)
null (requirement), and a
response (response):
-
If requirement is
null, return. -
Let required be the result of parsing a serialized connection allowlist requirement given requirement, response’s URL, and enforce.
-
If required is
null, return. -
If no connection allowlist in result’s connection allowlists whose disposition is enforce satisfies required, then append required to result’s connection allowlists.
-
Set result’s required connection allowlist to requirement.
Note: The embedder’s connection allowlist is added to result’s
connection allowlists as an additional entry, alongside any the
response asserted for itself (including `Connection-Allowlist-Report-Only`). Because every allowlist
in that list is enforced for the document’s outgoing requests, both the embedder’s requirement and the
document’s own assertions apply. Following [csp-embedded-enforcement], the requirement is
not appended when the response already asserts an allowlist that satisfies it, since that
assertion already meets the requirement and the extra entry is unnecessary.
Note: Storing the unresolved requirement on result’s policy container is what propagates it to
descendants: each descendant re-resolves the requirement (and its
response-origin token) against its own response.
A frame is therefore obligated to enforce the policy upon its children even when its own enforced
allowlist is stricter than the requirement. For Documents created from a
local scheme (such as about:srcdoc and data:), the policy container is
inherited from the embedder, so the requirement is inherited along with it.
The blocking decision is made in Fetch: a navigation response whose handshake fails is replaced with a network error. After a response is obtained for a navigation request (request) targeting a child navigable, if the result of determining whether the response should be blocked by Connection Allowlist embedded enforcement (given request and the navigation’s required connection allowlist) is blocked, the user agent returns a network error.
Fenced frames and other contexts whose requests are not governed by their embedder’s policy container cannot be constrained by this mechanism. Such a context MUST be blocked when its embedder requires a connection allowlist of it, rather than being allowed to load unconstrained. See § 6.2 Embedded Enforcement.
This integration deliberately parallels Content Security Policy: Embedded Enforcement § 2.4 Integration with HTML. The two mechanisms differ only in the policy type carried and in the "at least as strict as" comparison; ideally the shared scaffolding (the target snapshot params / navigation params item, the request-header hook, and the policy-container application) would be factored into HTML (or a shared embedded policy enforcement specification) and parameterized over the policy type. Aligning the two specifications on a single mechanism is tracked as follow-up.
5.3. DNS Prefetching
In the fetch and process the linked resource steps for the dns-prefetch link type, we’ll pass the link element’s node document to resolve an origin:
dns-prefetch link type, step 4 is updated as follows:
-
The user agent should resolve an origin given partitionKey, url’s origin, and el’s relevant settings object .
5.4. Preconnecting
In the preconnect algorithm, we’ll pass the link options' environment to obtain a connection:
-
The user agent should obtain a connection given partitionKey, url’s origin, useCredentials, and options’s environment .
5.5. Integration with WebRTC
To constrain WebRTC connections, [webrtc] can call into the should WebRTC be blocked by Connection Allowlists algorithm while determining whether candidates are administratively prohibited.
5.6. Integration with Service Workers
Beyond the default behavior of applying the service worker’s own policy container,
service workers need to apply their own connection allowlist when invoking a navigation using WindowClient APIs.
The navigate() and openWindow(url) algorithms must both be patched as follows:
-
If should url be blocked by Connection Allowlists returns blocked when executed
upon url, this’s relevant settings object, and this’s
relevant settings object’s policy container’s connection allowlists,
then return a promise rejected with a
"SecurityError"DOMException.
navigate() passes the current document as the sourceDocument of the navigation. This seems a bit like an oversight. See discussion.
6. Security and Privacy Considerations
6.1. Same-Origin Contexts
The threat model described in § 1.1 Threat Model is intentionally narrow, and developers will need
to carefully consider how to layer the allowlisting mechanism described here into their defenses.
Most saliently, the mechanism is context-specific, not origin-wide. This leaves broad opportunity
for an attacker with scripting access to bypass a context’s allowlist by finding a same-origin
context with lower restrictions. Integration with HTML’s policy container addresses some of
those possibilities, but it’s likely that others will exist. An attacker might, for example,
be able to reach up through the frame tree to a less-restricted parent, or pop open a new window
via window.open() which retains an opener relationship. Allowlisting the document’s origin
(via response-origin or explicitly), is therefore
not a complete solution in and of itself.
There are scenarios in which developers can avoid this risk by sandboxing the allowlisted context
away from its normal origin via sandbox attributes or Content Security Policy’s
sandbox directive. In those cases, no document will be same-origin, and the
boundaries will be easier to hold.
Developers may also use the embedded enforcement mechanism described in § 4 Embedded Enforcement to require a connection allowlist of the documents they frame, rather than relying on each framed document to constrain itself.
6.2. Embedded Enforcement
§ 4 Embedded Enforcement lets an embedder require a connection allowlist of a framed document, resolving issue #1. Several properties keep this from becoming a new attack surface:
-
Mandatory opt-in. An embedder cannot impose a policy upon unwilling cross-origin content. The framed document must either assert a `
Connection-Allowlist` which satisfies the requirement, or return an `Allow-Connection-Allowlist-From` header naming the embedder. Absent an opt-in (and absent delivery from a local scheme), the frame is blocked. This mirrors the concern that motivated requiring an opt-in for theiframecspattribute. [csp-embedded-enforcement] -
No relaxation on inheritance. The requirement is inherited by descendant frames, and a descendant’s own
connectionAllowlistattribute is honored only when it satisfies the inherited requirement. A constrained subframe therefore cannot escape its constraints by embedding a more permissive grandchild. For local scheme documents, an inherited allowlist is likewise never loosened by embedded enforcement. -
Conservative acceptance. A malformed asserted `
Connection-Allowlist` parses tonulland does not count as an opt-in, and the strictness comparison is a conservative syntactic set-membership test. A framed document therefore cannot satisfy a requirement it does not actually meet.
Contexts whose requests are not governed by their embedder’s policy container (for example, fenced frames) cannot be constrained by this mechanism. Such a context MUST be blocked when an embedder requires a connection allowlist of it, rather than being allowed to load unconstrained.
6.3. Service Workers
Service Workers complicate the story around allowlists, just as other same-origin contexts do. Because they have a policy container distinct from each of the documents they manage, it’s quite possible for them to respond to messages or requests initiated in documents whose allowlist differs from the service worker’s allowlist. This proposal follows other policies' design, allowing for these differences in capability.
If developers wish to constrain service workers' ability to make requests, they can deliver an allowlist along with the worker script, but they’ll need to ensure that this allowlist is a superset of the allowlists of any document it might service.
6.4. DNS
Connection Allowlists aim to mitigate the risk of exfiltration through DNS lookups, altering Fetch’s
resolve an origin algorithm to check the allowlist before sending a DNS request. This will
address both the implicit lookups resulting from the use of elements and APIs that send requests
(img tags, fetch(), etc), but also explicit lookups triggered by mechanisms like
dns-prefetch.
That said, the allowlist relies on URL-based matching to determine whether a given endpoint is acceptable. It is not a defense against DNS manipulation, it does not limit the DNS servers which are involved in a request (including the recursive nature of DNS resolution’s potential communication with multiple servers), nor does it restrict constructs like CNAME from causing additional lookups for domains outside the allowlist. Likewise, the allowlist cannot guarantee that the server DNS points towards is the server you might expect.
Developers should mitigate the risk of DNS hijacking and/or rebinding by relying upon authenticated connections: allowlisting only secure protocols will make it much more difficult for an attacker in control over DNS to shift traffic to an arbitrary endpoint, as the TLS handshake will require possession of a certificate with the relevant name.
Should we restrict the allowlist’s patterns to those representing secure protocols? Or punt the header entirely for non-secure origins?
6.5. postMessage(...)
This proposal concerns itself entirely with network connections, which may surprise developers
who would expect communication via explicit communication channels like
postMessage(message, options), MessageChannel, BroadcastChannel, and so on to
be covered. It could make sense to extend the model to include those as well, as they all fit
into an origin-based model which could be meaningfully compared against the allowlist.
6.6. Redirects
By default, Connection Allowlists block all redirects. This is a conservative posture which aims to prevent data exfiltration via open redirects or other server-side redirection mechanisms. If an allowlist is enforced on a document, any request that results in a redirect will be blocked unless the allowlist explicitly opts into allowing them.
The redirects parameter allows developers to control
this behavior.
If set to block (the default), any request
with a redirect chain length greater than 1 will be blocked.
If set to allow, the allowlist will be enforced
only on the initial request. If the initial request matches the allowlist, any subsequent redirect
will be allowed regardless of its location. This mode shifts the responsibility for data security
to the server: once a request has been allowed to leave the client, the server is responsible for
ensuring that it does not redirect the user’s data to an untrusted location.
This approach acknowledges that different applications have different security requirements. Highly sensitive applications can choose to block all redirects, while others can rely on their trusted endpoints to handle redirects correctly.
Connection-Allowlist: ("https://api.example")
A request to https://api.example/data that returns a 302 Found redirect to
https://api.example/new-data will be blocked, as redirects are blocked by default.
If the header is instead:
Connection-Allowlist: ("https://api.example");redirects=allow
The same request to https://api.example/data will be allowed, and the subsequent redirect
to https://api.example/new-data (or even https://attacker.com/) will also be allowed.
Finally, for forward-compatibility, an unknown token will be treated as allow:
Connection-Allowlist: ("https://api.example");redirects=some-future-policy
In this case, the redirects parameter is present but its value some-future-policy is unknown.
The user agent will treat this as allow, and the redirect will be allowed. This allows us
the possibility of introducing additional behaviors in the future without breaking existing
sites.
6.7. WebRTC
By default, Connection Allowlists block all WebRTC connections. This is a conservative posture intended to mitigate the risk of data exfiltration through WebRTC’s unique networking characteristics, which can be difficult to constrain via URL patterns alone.
The webrtc parameter allows developers to control
this behavior.
If set to block (the default), any attempt to
establish a WebRTC connection will be blocked.
If set to allow, WebRTC connections will be
allowed.
Connection-Allowlist: ("https://api.example")
Any attempt to establish a WebRTC connection will be blocked, as WebRTC is blocked by default.
If the header is instead:
Connection-Allowlist: ("https://api.example"); webrtc=allow
WebRTC connections will be allowed.
7. Implementation Considerations
7.1. WebSockets
It’s helpful to remember that WebSocket connections are covered by http and https patterns, not
by patterns specifying ws or wss schemes. When we establish a WebSocket connection, step 1
rewrites those websocket-specific schemes into http and https respectively. This means that
an allowlist containing patterns like "ws://socket.example" will not have the intended effect,
as that pattern will never be matched by a resource request.