Extend link HTTP header to support subresource signed exchange loading
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.3k
- Forks
- 125
- PR merge metrics
- No merged PRs in 30d
Description
I want to introduce two new fields in application/signed-exchange format.
- Alternative-Signed-Exchange-Subresources map in unsigned field.
- Allowed-Alternative-Signed-Exchange-Subresources list in signed field.
Problem
Currently content publishers can sign their HTML contents using their own private keys. User Agents (UAs) can trust the signed contents as if the contents are served from the publisher’s origins even if they are served from other distributors’ origins. The signed contents can be served from any distributors’ origins. But if the publisher wants to serve subresources such as scripts and images from the distributors’ origin, the publisher needs to change the subresource URLs in the HTML to point to each distributors’ URL and need to sign for each distributor. The proposed two fields can solve this problem.
Alternative-Signed-Exchange-Subresources map:
A map from the original subresource requests to the SXG URLs. This field is not signed. So the distributor can change this field to point to their URLs.
Allowed-Alternative-Signed-Exchange-Subresources list:
The subresource URL list which can be served using SXG instead of fetching the original URL. This field is signed by the publisher. So the distributor can’t change this field.
Example
Publisher: https://publisher.example/article_1.html
<script src="framework.js"></script>
<img src="article_1.jpg>
SXG in Publisher: https://publisher.example/article_1.html.sxg
[
// URL
'https://publisher.example/article_1.html',
// Signature
'sig1: sig=*...; integrity="digest/mi-sha256";cert-url="https://publisher.example/cert"',
// [New field] Alternative-Signed-Exchange-Subresources
// The key of the mapping may need Accept headers info in order to enable content
// negotiation (e.g. for WebP).
[
[{':url': 'https://publisher.example/framework.js', 'accept': '*/*'},
'https://publisher.example/framework.js.sxg'],
[{':url': 'https://publisher.example/article_1.jpg', 'accept': '*/*'},
'https://publisher.example/article_1.jpg.sxg']
],
// Signed headers
[
{ ':method': 'GET', 'accept': '*/*' },
{
':status': '200',
// [New field]
':allowed-alternative-signed-exchange-subresources':
'"https://publisher.example/framework.js",'
'"https://publisher.example/article_1.jpg"',
'content-encoding': 'mi-sha256-03',
'content-type': 'text/html; charset=utf-8',
'digest': 'mi-sha256-03=....'
},
],
// Payload body
'<html><body>...'
]
SXG in Distributor: https://distributor.example/article_1.html.sxg
[
// URL
'https://publisher.example/article_1.html',
// Signature
'sig1: sig=*...; integrity="digest/mi-sha256";cert-url="https://distributor.example/publisher.example/cert"',
// [New field] Alternative-Signed-Exchange-Subresources
[
[{':url': 'https://publisher.example/framework.js', 'accept': '*/*'},
'https://distributor.example/publisher.example/framework.js.sxg'],
[{':url': 'https://publisher.example/article_1.jpg', 'accept': '*/*'},
'https://distributor.example/publisher.example/article_1.jpg.sxg']
],
// Signed headers (Same as the SXG in Publisher)
[
{ ':method': 'GET', 'accept': '*/*' },
{
':status': '200',
// [New field]
':allowed-alternative-signed-exchange-subresources':
'"https://publisher.example/framework.js",'
'"https://publisher.example/article_1.jpg"',
'content-encoding': 'mi-sha256-03',
'content-type': 'text/html; charset=utf-8',
'digest': 'mi-sha256-03=....'
},
],
// Payload body (Same as the SXG in Publisher)
'<html><body>...'
]
How UAs should work
- When the user opens the SXG in the distributor, the UA must check the signature using the certificate in https://distributor.example/publisher.example/cert. (This is the existing behavior)
- The UA processes the script tag and the img tag and decides to fetch the "framework.js" and the image "article_1_small.jpg".
- Instead of fetching the original URL in publisher.example, UA should fetch the SXG files in distributor.example after checking the
Alternative-Signed-Exchange-Subresourcesfield and theAllowed-Alternative-Signed-Exchange-Subresourcesfield. - If the original URL is not in the
Allowed-Alternative-Signed-Exchange-Subresourcesfield, the UA must fetch the original URL. This is intended to avoid the subresource monitoring attack. - If the original URL’s origin is not same as the signed origin of the main SXG (publisher.example), the UA must fetch the original URL. This restriction is intended to avoid providing a way of tracking.
- UAs should handle the preload link header in the signed response header in the same way. (ex:
link: <https://example.com/framework.js>;rel="preload";as="script")
Subresource monitoring attack
We need the signed Allowed-Alternative-Signed-Exchange-Subresources to avoid the subresource monitoring attack like this:
- A publisher generates a SXG of a html which shows the user's icon using JS.
icon.src = USER_ID + '.png'; - An attacker sets the mapping info like this:
{ 'example.com/a.png': 'attacker.com/a.png.sxg', 'example.com/b.png': 'attacker.com/b.png.sxg', ....} - If UA fetches the SXG of png when the image tag is added, the attacker can know the USER_ID. Even if UA only uses the prefetched SXG, the attacker distributor can intentionally delay returning the SXGs one by one to see when the load actually finishes by monitoring the onload event, therefore can know the USER_ID.
Tracking using subresource SXG
We need to prohibit the SXG loading for cross-origin subresources to avoid the user tracking like this:
- A publisher sets one subresource to the
Allowed-Alternative-Signed-Exchange-Subresourcesfield (https://tracking.example/id.js) - The distributor server can let the publishers’ site know about the user’s ID (ABCD1234) by changing the
Alternative-Signed-Exchange-Subresourcesfield. - tracking.example/id.js points to tracking.example/ABCD1234.sxg (body is `const id='ABCD1234';)
Tracking is still possible even if we prohibit cross-origin subresources using the following logic. But this is more difficult.
- A publisher sets 30 subresources to the
Allowed-Alternative-Signed-Exchange-Subresourcesfield (https://publisher.example/00, 01, ... 29) - The publisher prepares 60 files, 00_0.sxg (body is 0), 00_1.sxg (body is 1), 01_0.sxg (body is 0), 01_1.sxg (body is 1)...
- The distributor server can let the publishers’ site know about the user’s ID in binary digits by changing the
Alternative-Signed-Exchange-Subresourcesfield.- publisher.example/00 points to 00_0.sxg or 00_1.sxg
- publisher.example/01 points to 01_0.sxg or 01_1.sxg
- ....
- This logic provides a way of user tracking of 2^30 users.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the proposed Alternative-Signed-Exchange-Subresources and Allowed-Alternative-Signed-Exchange-Subresources fields, then review the issue discussion for an agreed direction. Done requires an accepted format and user-agent behavior for subresource and preload loading, including the stated monitoring and tracking constraints.
Written by the indexing model from the issue text.
Assessment
- Domain
- security, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100