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 depedencies.
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, ny 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 soley 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, 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 § 5.5 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 § 5.6 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:
-
If URL pattern matching given pattern and url does not return
null, return success.
-
-
Return failure.
-
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. Monkey-Patches
4.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:
- If should host be blocked by Connection Allowlists returns blocked when executed upon origin’s host, environment, and allowlists, then return failure.
- 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.
4.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.
4.3. 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. Security and Privacy Considerations
5.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.
It would also be ideal to give developers control over their dependencies' allowlists to some extent. An opt-in mechanism rooted in something like required document policy or [csp-embedded-enforcement]] might be helpful to explore. [WICG/connection-allowlists Issue #1]
5.2. 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.
5.3. DNS
Connection Allowlists rely on URL matching to determine whether a given endpoint is acceptable. This approach, like any other name-based mechanism, can be subverted if the name maps to an unexpected server. The allowlist depends on an accurate mapping of hosts to servers in order for the constraints it imposes to be meaningful.
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?
5.4. 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.
5.5. 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.
5.6. 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.