You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If the fetch client has COEP:unsafe-none and the response is fetched again via CacheStorage from a client with COEP:require-corp, we run again the CORP check on the cross-origin no-cors response and potentially block it. This avoids a cross-origin no-cors response from entering a crossOriginIsolated process without an explicit opt-in from the server.
The current spec is:
1. [=list/For each=] |response| of |responses|:
1. If |response|'s [=response/type=] is "`opaque`" and [=cross-origin resource policy check=] with |promise|'s [=relevant settings object=]'s [=environment settings object/origin=], |promise|'s [=relevant settings object=], and |response|'s [=internal/internal response=] returns <b>blocked</b>, then reject |promise| with a `TypeError` and abort these steps.
We have the same question for COEP:credentialless (see whatwg/html#6637). How to prevent credentialled opaque responses from entering the crossOriginIsolated context?
This is a bit different, because COEP:credentialless is about the request and COEP:require-corp about the response.
Possibilities could be:
Do not provide CacheStorage to COEP:credentialless context.
Same implementation of COEP:require-corp, run the cross-origin resource policy check, but require corp.
Store the embedder policy of the original client in the stored response. Block opaque responses requested from COEP:unsafe-none into COEP:credentialless. We can also potentially allow the one passing cross-origin resource policy check with require-corp.
I think an important thing to consider here is that typically these cache responses are returned to fetch, which will run its CORP check in step 7 of https://fetch.spec.whatwg.org/#concept-http-fetch. So I don't think 2 will work (at least not without adding further complexity elsewhere).
Assuming we want to treat the policies in an equivalent manner (still a bit unclear), perhaps storing a bit on opaque responses as to whether they are "COEP friendly" is a way to go here. Not allowing opaque responses would be nicer still, but I'm not sure how feasible that is.
Assuming we want to treat the policies in an equivalent manner (still a bit unclear), perhaps storing a bit on opaque responses as to whether they are "COEP friendly" is a way to go here.
Thanks!
Yes, (3) was more or less my primary intuition. Not the easiest thought.
I will add some tentative WPT tests about about CacheStorage. Then I will prototype to verify this is possible to make something reasonable out of this.
I prototyped plumbing the request's includeCredentials (from the HTTP fetch algorithm) into the stored response.
If during the Cache.match algorithm:
The response is opaque.
The response's requestIncludeCredentials is true
The client embedder policy is credentialless
Then, we have two options:
Return an error directly.
Run the CORP check algorithm with require-corp and return an error if it fails.
(1) is simpler and stricter.
(2) is more permissive. The response requested with credentials are allowed if they pass the CORP check.
I am not sure it really matter. CacheStorage is a per-origin API. I am expecting website to use a consistent COEP policy for the whole website & workers. I don't believe there will be a strong use case for using CacheStorage with different COEP policies. So I believe the strictest option (1) to be better. We can always make it more permissive later, if we feels this is useful.
Does that seems reasonable to you?
Note: Here are the expectations for both cases on the test cache-storage.tentative.https.html. The test makes a cross-origin, no-cors, credentials: include request from one context and try to retrieve it from another one with a different COEP policy.
I was thinking about a different model, whereby we give an opaque response a bit (tentatively named "COEP safe") if it was fetched in the context of a COEP policy. And then if you obtain something from the cache in the context of a COEP policy and you get an opaque response, you'd error if the bit isn't set.
Fetch would also have to be updated to check for this for responses from the service worker and to set this correctly when fetching something.
I think this is equivalent to your less strict version. I have a slight preference for this as it seems like the logic would be simpler for anyone having to handle these kind of responses and it would be more consistent with how we treat the policies as interchangeable elsewhere (e.g., nested documents).
To clarify what you meant, what would give those cases? (I am assuming here that coep_safe: "original client embedders's policy != none")
Case \ params
request, no-cors, cross-origin
response
original client
second client
coep_safe (?)
result (?)
1
credentials=include
COEP:none
COEP:credentialless
false
error
2
credentials=include
CORP:cross-origin
COEP:require-corp
COEP:credentialless
true
allow
3
credentials=include
CORP:cross-origin
COEP:none
COEP:credentialless
false
error
4
credentials=omit
COEP:none
COEP:credentialless
false
error
5
credentials=include
CORP:cross-origin
COEP:none
COEP:require-corp
false
error
(1) & (2) are just the base cases with COEP:none and COEP:require-corp. So far so good. This sounds right.
(3) is stricter than my "less strict version" in #1592 (comment)
(4) is stricter than both proposition from #1592 (comment)
(5) contradict with today's behavior. Maybe you didn't meant this to apply to COEP:require-corp client instead?
and it would be more consistent with how we treat the policies as interchangeable elsewhere (e.g., nested documents).
Indeed, that would be a nice thing to keep if can can.
Thank you for writing that up, I see what I overlooked now. My approach would only work if the original client had COEP and so "COEP safe" is not realistic. I'm actually not sure what's ideal then, but storing whether it was obtained with credentials doesn't seem that bad.
@mfalken and I caught up with this thread. Thanks for the detailed explanations. I wasn't really aware of COEP:credentialless before reading this thread, so there's a chance I'm misunderstanding the whole thing.
I think our goal should be:
Case \ params
request, no-cors, cross-origin
response
original client
second client
result
1
credentials=include
COEP:none
COEP:credentialless
error
2
credentials=include
CORP:cross-origin
COEP:require-corp
COEP:credentialless
allow
3
credentials=include
CORP:cross-origin
COEP:none
COEP:credentialless
allow
4
credentials=omit
COEP:none
COEP:credentialless
allow
5
credentials=include
CORP:cross-origin
COEP:none
COEP:require-corp
allow
It feels like this could be achieved by the requestIncludedCredentials idea mentioned previously, then changing https://fetch.spec.whatwg.org/#cross-origin-resource-policy-internal-check to check for that. That algorithm is run against responses coming from network, service workers, and cache.match.
@jakearchibald
Yes, I believe something like this could work and this would give the expectations we would like to see above.
I am almost done with a prototype in Chrome and this looked reasonable.
I am on vacation until the 25th. Once back, I will update the fetch PR with your step (5) for cross-origin-policy-internal-check + setting the request included credentials attribute. Thanks for your indications!
Hi I am back from vacation. I prototyped what was discussed in Chrome and modified the fetch PR accordingly. The interesting part is the CORP check:
From:
If policy is null and embedderPolicyValue is require-corp, set policy to same-origin
To:
If policy is null, switch on embedderPolicyValue:
unsafe-none
credentialless
Set policy to same-origin if one of the following is true:
response’s request-include-credentials is true and response’s type is opaque.
forNavigation is true.
require-corp
Set policy to same-origin.
This change passes all the test cases defined from the table above, and the corresponding WPT.
This requires CORP when opaque responses are containing credentials (only possible via CacheStorage).
It isn't impacted. I wrote it the same way it is done in the fetch spec, where we assume there is an implicit break for every case of the switch statement.
That's not how that construct works in my mind, though good to know it can be interpreted that way. As far as I know it's meant to follow dt/dd grouping logic, which would mean that unsafe-none and credentialless end up with the same set of steps.
credentialless
Set policy to same-origin if one of the following is true:
response’s request-include-credentials is true and response’s type is opaque (*).
forNavigation is true.
We don't need the latter half of (*) because it's already checked at the callers.
If response’s type is "opaque" and cross-origin resource policy check with promise’s relevant settings object's origin, promise’s relevant settings object, "", and response’s internal response returns blocked, then reject promise with a TypeError and abort these steps. https://w3c.github.io/ServiceWorker/#cache-matchall
If either request’s response tainting or response’s type is "opaque", and the cross-origin resource policy check with request’s origin, request’s client, request’s destination, and actualResponse returns blocked, then return a network error. https://fetch.spec.whatwg.org/#concept-http-fetch
Yes that true. I got confused with the Chrome implementation (where response_type is not computed yet for fetch) and my understanding of opaque response was a bit different than the reality (I didn't know about tainted_origin for cross-origin redirect).
Since the CORP check is not only run for CacheStorage, but also for normal fetch request, the current proposition would break normal fetch request. I should find a way to filter out:
same-origin response,
non-final response,
non no-cors response.
Or alternatively not run this in the CORP check and make this specific to CacheStorage. I will have to rework this, find a solution and get back to you with something better.
Since the CORP check is not only run for CacheStorage, but also for normal fetch request, the current proposition would break normal fetch request.
I'm struggling to follow this a bit. I thought it was a good thing that the check included normal fetch requests. Can you walk me through a case where this breaks?
Since the CORP check is not only run for CacheStorage, but also for normal fetch request, the current proposition would break normal fetch request.
I'm struggling to follow this a bit. I thought it was a good thing that the check included normal fetch requests. Can you walk me through a case where this breaks?
For the network fetch nothing should be blocked because we omit the credentials. On the other hand, for responses coming from the service worker we need a similar check, so it may make sense to have the logic in the CORP check to not have duplicated logic.
I feel we can close this issue now. It solves the issue with CacheStorage, but also for ServiceWorkers. I am happy with it.
Feel free to re-open if needed.
So for COEP:credentialless context, a no-cors subresource requires either a CORP header or to be requested without credentials. It means when a response is transferred from a COEP:unsafe-none context toward a COEP:credentialless, then it might be blocked.
Activity
annevk commentedon May 5, 2021
I think an important thing to consider here is that typically these cache responses are returned to fetch, which will run its CORP check in step 7 of https://fetch.spec.whatwg.org/#concept-http-fetch. So I don't think 2 will work (at least not without adding further complexity elsewhere).
Assuming we want to treat the policies in an equivalent manner (still a bit unclear), perhaps storing a bit on opaque responses as to whether they are "COEP friendly" is a way to go here. Not allowing opaque responses would be nicer still, but I'm not sure how feasible that is.
yutakahirano commentedon May 6, 2021
+1
ArthurSonzogni commentedon May 6, 2021
Thanks!
Yes, (3) was more or less my primary intuition. Not the easiest thought.
I will add some tentative WPT tests about about CacheStorage. Then I will prototype to verify this is possible to make something reasonable out of this.
[Credentialless]: WPT vs CacheStorage.
[Credentialless]: WPT vs CacheStorage.
[Credentialless]: WPT vs CacheStorage.
[Credentialless]: WPT vs CacheStorage.
[Credentialless]: WPT vs CacheStorage.
Bug 1710016 [wpt PR 28891] - [Credentialless]: WPT vs CacheStorage., …
ArthurSonzogni commentedon May 11, 2021
We now have some tentative tests (49 cases) for Document, SharedWorker, ServiceWorker, and DedicatedWorker:
https://wpt.fyi/results/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https.html?label=experimental&label=master&aligned
(Firefox already has 4 passing tests more than Chrome about
require-corp. Great job! Those will be fixed very soon by plz-{DedicatedWorker, SharedWorker})I prototyped plumbing the
request'sincludeCredentials(from the HTTP fetch algorithm) into the storedresponse.If during the
Cache.match algorithm:responseisopaque.response'srequestIncludeCredentialsis trueembedder policyiscredentiallessThen, we have two options:
CORP check algorithmwithrequire-corpand return an error if it fails.(1) is simpler and stricter.
(2) is more permissive. The response requested with credentials are allowed if they pass the CORP check.
I am not sure it really matter. CacheStorage is a per-origin API. I am expecting website to use a consistent COEP policy for the whole website & workers. I don't believe there will be a strong use case for using CacheStorage with different COEP policies. So I believe the strictest option (1) to be better. We can always make it more permissive later, if we feels this is useful.
Does that seems reasonable to you?
Note: Here are the expectations for both cases on the test
cache-storage.tentative.https.html. The test makes a cross-origin,no-cors,credentials: includerequest from one context and try to retrieve it from another one with a different COEP policy.annevk commentedon May 11, 2021
I was thinking about a different model, whereby we give an opaque response a bit (tentatively named "COEP safe") if it was fetched in the context of a COEP policy. And then if you obtain something from the cache in the context of a COEP policy and you get an opaque response, you'd error if the bit isn't set.
Fetch would also have to be updated to check for this for responses from the service worker and to set this correctly when fetching something.
I think this is equivalent to your less strict version. I have a slight preference for this as it seems like the logic would be simpler for anyone having to handle these kind of responses and it would be more consistent with how we treat the policies as interchangeable elsewhere (e.g., nested documents).
ArthurSonzogni commentedon May 11, 2021
Yes, "coep_safe" might work too.
To clarify what you meant, what would give those cases?
(I am assuming here that coep_safe: "original client embedders's policy != none")
(1) & (2) are just the base cases with
COEP:noneandCOEP:require-corp. So far so good. This sounds right.(3) is stricter than my "less strict version" in #1592 (comment)
(4) is stricter than both proposition from #1592 (comment)
(5) contradict with today's behavior. Maybe you didn't meant this to apply to COEP:require-corp client instead?
Indeed, that would be a nice thing to keep if can can.
annevk commentedon May 11, 2021
Thank you for writing that up, I see what I overlooked now. My approach would only work if the original client had COEP and so "COEP safe" is not realistic. I'm actually not sure what's ideal then, but storing whether it was obtained with credentials doesn't seem that bad.
jakearchibald commentedon May 14, 2021
@mfalken and I caught up with this thread. Thanks for the detailed explanations. I wasn't really aware of
COEP:credentiallessbefore reading this thread, so there's a chance I'm misunderstanding the whole thing.I think our goal should be:
It feels like this could be achieved by the
requestIncludedCredentialsidea mentioned previously, then changing https://fetch.spec.whatwg.org/#cross-origin-resource-policy-internal-check to check for that. That algorithm is run against responses coming from network, service workers, andcache.match.So, after step 4 of https://fetch.spec.whatwg.org/#cross-origin-resource-policy-internal-check:
Does that work? I guess request included credentials would be set around the same time as the range requested flag.
ArthurSonzogni commentedon May 15, 2021
@jakearchibald
Yes, I believe something like this could work and this would give the expectations we would like to see above.
I am almost done with a prototype in Chrome and this looked reasonable.
I am on vacation until the 25th. Once back, I will update the fetch PR with your step (5) for
cross-origin-policy-internal-check+ setting therequest included credentialsattribute. Thanks for your indications!7 remaining items
Bug 1712758 [wpt PR 29102] - [Credentialless]: WPT vs CacheStorage. 2…
ArthurSonzogni commentedon May 31, 2021
Hi I am back from vacation. I prototyped what was discussed in Chrome and modified the fetch PR accordingly. The interesting part is the CORP check:
From:
require-corp, set policy tosame-originTo:
unsafe-nonecredentiallessSet policy to
same-originif one of the following is true:request-include-credentialsis true and response’s type isopaque.require-corpSet policy to
same-origin.This change passes all the test cases defined from the table above, and the corresponding WPT.
This requires CORP when opaque responses are containing credentials (only possible via CacheStorage).
I believe I am happy with that. What about you?
annevk commentedon May 31, 2021
Why is
unsafe-noneimpacted?ArthurSonzogni commentedon May 31, 2021
It isn't impacted. I wrote it the same way it is done in the fetch spec, where we assume there is an implicit break for every case of the switch statement.
annevk commentedon May 31, 2021
That's not how that construct works in my mind, though good to know it can be interpreted that way. As far as I know it's meant to follow
dt/ddgrouping logic, which would mean thatunsafe-noneandcredentiallessend up with the same set of steps.ArthurSonzogni commentedon May 31, 2021
You are right. I read to quickly the existing usage. I should insert a "Do nothing" instruction to remove the ambiguity. => Done.
yutakahirano commentedon Jun 2, 2021
We don't need the latter half of (*) because it's already checked at the callers.
ArthurSonzogni commentedon Jun 2, 2021
Yes that true. I got confused with the Chrome implementation (where response_type is not computed yet for fetch) and my understanding of opaque response was a bit different than the reality (I didn't know about tainted_origin for cross-origin redirect).
Since the CORP check is not only run for CacheStorage, but also for normal fetch request, the current proposition would break normal fetch request. I should find a way to filter out:
Or alternatively not run this in the CORP check and make this specific to CacheStorage. I will have to rework this, find a solution and get back to you with something better.
jakearchibald commentedon Jun 4, 2021
I'm struggling to follow this a bit. I thought it was a good thing that the check included normal fetch requests. Can you walk me through a case where this breaks?
yutakahirano commentedon Jun 4, 2021
For the network fetch nothing should be blocked because we omit the credentials. On the other hand, for responses coming from the service worker we need a similar check, so it may make sense to have the logic in the CORP check to not have duplicated logic.
ArthurSonzogni commentedon Jun 4, 2021
@jakearchibald, @yutakahirano
I agree with both of you. I got confused (again!) about redirects due to a minor bug in the implementation.
Keeping the check in CORP (as Yutaka suggest) sounds good. And removing the unnecessary check about response_type = "opaque" is also good.
This would lead to this change to the CORP check:
https://htmlpreview.github.io/?https://github.com/mikewest/credentiallessness/blob/master/index.html#corp-internal-check
[Credentialless] Block credentialled opaque response (CacheStorage)
ArthurSonzogni commentedon Jul 1, 2021
I feel we can close this issue now. It solves the issue with CacheStorage, but also for ServiceWorkers. I am happy with it.
Feel free to re-open if needed.
So for COEP:credentialless context, a no-cors subresource requires either a CORP header or to be requested without credentials. It means when a response is transferred from a COEP:unsafe-none context toward a COEP:credentialless, then it might be blocked.
Bug 1710016 [wpt PR 28891] - [Credentialless]: WPT vs CacheStorage., …