In a Content-Security-Policy source list, which URLs does the source expression * match, and which does it miss?
answer
- a wildcard with a scope
- network URLs only
- data: and blob: must be named
- subdomain wildcard is not the apex
- scheme matching upgrades, never downgrades
basics
~20 sA bare * matches any host and port over HTTP(S), plus URLs in the document's own scheme. It does not reach data:, blob: or filesystem: URLs - those only load if the policy names that scheme explicitly.
solid answer
~40 s`*` written as a whole source expression is a wildcard over **network** URLs: any host, any port, over `http` and `https`, plus the document's own scheme. It is deliberately not a wildcard over *every* URL - `data:`, `blob:` and `filesystem:` URLs are excluded, and permitting one means writing the scheme-source `data:` or `blob:` into the list. A wildcard may also appear inside a host-source's host-part, as in `https://*.example.net`, which matches subdomains but not the bare apex. One more asymmetry is worth carrying: scheme matching upgrades but never downgrades, so the scheme-source `http:` also permits `https:` URLs, while `https:` never permits `http:`. The same asymmetry applies to a host-source whose scheme-part is `http`.
code
http · 3 linesHTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Security-Policy: default-src 'none'; connect-src *; style-src 'self' https://*.example.netgo deeper
Recall that the wildcard is about hosts and ports over the network, and that data: and blob: sources have to be named by scheme before they load.
Explain the parts of a host-source, why a subdomain wildcard excludes the apex, and why scheme matching runs in the upgrade direction only.
In review, ask which directive the wildcard sits in and what problem it was added to solve - a wildcard added for a scheme refusal buys nothing and costs the whole host space.
Weigh the standing cost: a wildcard is an allowlist nobody can audit later, and the organisational question is whether any directive may carry one at all.
## What the bare wildcard covers Written as a whole source expression, `*` is the broadest thing you can put in a `Content-Security-Policy` source list - but it is broad over **network** URLs only. It matches: - any host, including hosts you have never heard of - any port - `http` and `https` URLs, and URLs whose scheme is the document's own scheme That is already enough to give up most of what the directive was for, which is why `*` in a `script-src` is a policy that mostly exists to say a policy exists. The interesting part for reading a policy correctly is what it still refuses. ## What the wildcard misses A bare `*` does **not** match `data:`, `blob:` or `filesystem:` URLs. These are not fetched over the network and are not covered by a wildcard over hosts; the only way to permit them is to name the scheme: ```http Content-Security-Policy: default-src 'none'; connect-src *; style-src 'self' blob: ``` This surprises people in exactly one direction: a page under `connect-src *` still sees a `blob:` target refused, and the author concludes the policy is broken when it is doing precisely what it says. The same rule is why a `blob:` URL is not covered by `'self'` either, even though the document created it. ## Wildcards inside a host-source A host-source has up to four parts - a scheme-part, a host-part, a port-part and a path-part - and the wildcard may appear in two of them: | expression | matches | does not match | |---|---|---| | `https://*.example.net` | `https://tiles.example.net`, `https://a.b.example.net` | `https://example.net` - the apex is not a subdomain | | `https://tiles.example.net:*` | that host on any port | any other host | | `*` | any host, any port, HTTP(S) | `data:`, `blob:`, `filesystem:` | The apex row is the one that costs an afternoon. `*.example.net` is a wildcard over **subdomains**; if the page also loads from the bare domain, the bare domain has to be written out as well. ## Scheme matching is deliberately asymmetric A scheme-source is a scheme followed by a colon - `https:` - and matching it is not symmetric: - `script-src http:` also permits `https:` URLs. Allowing the weaker scheme implies the stronger one, so a policy written before a site finished its migration keeps working afterwards. - `script-src https:` never permits `http:` URLs. There is no path from the stronger allowance to the weaker one. The same asymmetry applies to the scheme-part of a host-source: `http://tiles.example.net` also matches the `https` URL of that host, while `https://tiles.example.net` does not match the `http` one. The rule is the same everywhere in the grammar - **upgrade, never downgrade** - and it is there so that tightening the transport of your dependencies never requires re-editing the policy. ## Two more grammar details in the same family - **A source expression with no scheme-part** inherits its scheme comparison from the protected document, so `tiles.example.net` inside a policy on an `https` page is about the `https` URL of that host, with the upgrade rule still applying. - **An internationalised host name must be written in its Punycode form** in the policy. A host written in non-ASCII characters will not match the URL the browser is actually fetching, and the refusal looks like a mystery because the two strings appear identical on screen. ## How to read a wildcard in review When a policy comes past you with a `*` in it, ask two questions in order. First, *which directive is it in* - a wildcard in a directive governing executable code is a different proposition from one governing a connection to an unpredictable set of endpoints. Second, *was the wildcard written to solve a scheme problem?* A wildcard added because `data:` or `blob:` was being refused does not fix that at all, and the author has widened the network side of the directive to every host on the internet in exchange for nothing. Naming the scheme is the change that was actually needed.
- Does the source expression https://*.example.net permit a stylesheet from https://example.net?No. A wildcard in the host-part matches subdomains, and the bare apex is not a subdomain of itself. If the page loads from both, the policy must list the apex separately alongside the wildcard expression.
- Why does script-src http: also permit an https URL, while script-src https: never permits an http one?Scheme matching is deliberately asymmetric in the upgrade direction: allowing the weaker transport implies the stronger. That way a policy written during a migration keeps working once the dependency moves to the secure scheme, without loosening anything.
saying these in an interview costs you the question
- Thinks a bare * permits data: and blob: URLs too
- Expects *.example.net to cover the bare apex domain
- Believes https: also permits the plaintext URL of a host
- Adds a wildcard to fix a refused blob: target
- Writes an internationalised host name without Punycode encoding