attribution-reporting-api icon indicating copy to clipboard operation
attribution-reporting-api copied to clipboard

Set report request's origin to reporting endpoint

Open apasel422 opened this issue 2 years ago • 16 comments

Otherwise, the default of "client" interacts incompatibly with the setting of the request's client to null.

Fixes #546


Preview | Diff

apasel422 avatar Aug 25 '22 16:08 apasel422

CC @domfarolino

apasel422 avatar Aug 25 '22 16:08 apasel422

Would using an opaque origin be better? Not sure what the implications are either way.

apasel422 avatar Aug 25 '22 16:08 apasel422

The implications are basically if the request should be seen as a cross-origin request or not, which would impact CORS of course, since the request mode is CORS. I'm not sure if there are any other implications.

domfarolino avatar Aug 25 '22 22:08 domfarolino

The implications are basically if the request should be seen as a cross-origin request or not, which would impact CORS of course, since the request mode is CORS. I'm not sure if there are any other implications.

Given that the request is sent without credentials and we never inspect the response beyond the HTTP status code, what are the practical implications for this being CORS vs non-CORS?

apasel422 avatar Aug 26 '22 12:08 apasel422

If you request with CORS, then the server must respond with a suitable Access-Control-Allow-Origin header/value for the response to be readable. If the server doesn't do this, then I believe you get a CORS filtered response, which has a status code 0. Technically you can still get at the "real" response by inspecting the internal response, but that's basically just bypassing CORS in which case you should probably not make a CORS request.

If you make a "no-cors" request, then the server won't be able to deny access to the response simply by omitting CORS headers. That's the simplest approach, but using "no-cors" is very much not recommended. A couple of questions:

  • Who triggers the request? Is script from a cross-site document?
  • Who reads the response (or status code since it sounds like that's all you care about)? Is it just browser-side mechanisms that don't share a process with any cross-site script?

I think problems come into play with "no-cors" specifically whenever it could be used to poke a hole in the same-origin policy. That's why I'm asking who is responsible for triggering/reading the request/response. My gut tells me that if this is a "browser process"-issued request, and only ever consumed by the "browser process", then we might be OK with "no-cors" but I don't want to lead you down the wrong path. There has been recent discussion about CORS vs no CORS recently over in https://github.com/fedidcg/FedCM/issues/320 for requests that I believe are of this nature.

domfarolino avatar Aug 26 '22 21:08 domfarolino

  • Who triggers the request? Is script from a cross-site document?

The browser triggers the request in the background, at a random point after attribution has occurred. The request isn't associated with a document or script.

  • Who reads the response (or status code since it sounds like that's all you care about)? Is it just browser-side mechanisms that don't share a process with any cross-site script?

The browser reads the status code. No data is shared with any document or script.

apasel422 avatar Aug 29 '22 13:08 apasel422

I see. I don't want to lead anyone down the wrong path, so I'd suggest just reaching out to security reviewers (presumably this feature has been looked at by Chrome OWP Security folks) that know more about the whole flow of ad attribution and the subsequent reporting for a final say, which I realize is not a super duper helpful or satisfactory answer.

I just don't know enough about what attribute is to understand how this request is triggered and what all parties are involved. While my knee-jerk reaction is that the request appears safe with "no-cors", given that it's an anti-pattern I want to make sure the surrounding context here meets a high enough bar for security-minded people to be OK with that.

domfarolino avatar Aug 30 '22 16:08 domfarolino

cc @letitz who looked at this API from the security side. Would you mind helping us decide what is best here?

csharrison avatar Sep 19 '22 21:09 csharrison

Thanks for tagging me. I agree with @domfarolino that we should probably land on something coherent with what FedCM is doing.

I went back to look at our discussion notes on this and found the following: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/attribution_reporting/attribution_network_sender_impl.cc;l=88-98;drc=792ebb6b13653738568433f64cea85b5bdf6b203

The request uses a transient NIK, how does that map to fetch spec concepts?

letitz avatar Sep 21 '22 10:09 letitz

Thanks for tagging me. I agree with @domfarolino that we should probably land on something coherent with what FedCM is doing.

I went back to look at our discussion notes on this and found the following: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/attribution_reporting/attribution_network_sender_impl.cc;l=88-98;drc=792ebb6b13653738568433f64cea85b5bdf6b203

The request uses a transient NIK, how does that map to fetch spec concepts?

I believe the NIK's spec concept is the network partition key in Fetch. In our case it is null because our request has a null client (see 11.4.2) which corresponds to the transient NIK IIUC.

csharrison avatar Sep 21 '22 13:09 csharrison

Thanks! I went looking for the uses of a fetch request's origin in the Fetch spec, and found that it mostly applies to CORS and CORP checks. Given that the response never makes it to the renderer process, it seems pointless to apply CORS and CORP checks to reporting requests, so this PR LGTM from the point of view of Chrome security.

OTOH, the above argument makes assumptions about browser architecture that might not hold in a cross-browser spec context. You might want to reach out to folks at other browsers to ask them what they think, or participate in the FedCM discussion as interested onlookers?

letitz avatar Sep 21 '22 15:09 letitz

In our case I think a CORS mode fetch with the request's origin set to the reporting endpoint (what this PR does) makes the most sense. While the request isn't associated with a document or script, it conceptually is triggered by the reporting endpoint itself via response headers issued on previous requests. Another site can never[^1] cause these browser-mediated requests to be invoked.

I will follow along with the FedCM discussion but in their case the browser-mediated request in question (accounts endpoint) is triggered by a third party (the RP) via the login flow.

[^1]: Unless, currently, the previous request to cause the future browser-mediated request was made with proper Access-Control-Allow-Origin headers and the header was injected via a cross origin service worker. However, we are planning on changing this so requests are not modifiable by service workers in https://github.com/WICG/attribution-reporting-api/issues/550

csharrison avatar Sep 23 '22 15:09 csharrison

Could you walk me through the flow that actually results in the request being sent?

domfarolino avatar Oct 12 '22 15:10 domfarolino

  1. A network request for source registration is made to the reporting origin, on site A. The reporting origin uses response headers to register a source.
  2. A network request for trigger registration is made to the reporting origin, on site B. The reporting origin uses response headers to trigger attribution.
  3. If the request in (2) "matches" the request in (1), the browser will generate a report combining information from (1) and (2) and schedule a report to a .well-known on the reporting origin's server.

csharrison avatar Oct 12 '22 15:10 csharrison

Can't a random site just click a link and then trigger a request to the reporting origin? I'll admit I don't have a lot of context here so I don't want to muddy the waters too much with super basic/dumb questions I have. I'll leave the final call on the request's origin to @letitz/security folks who presumably have done or are doing a more comprehensive review.

domfarolino avatar Oct 12 '22 17:10 domfarolino

Can't a random site just click a link and then trigger a request to the reporting origin? I'll admit I don't have a lot of context here so I don't want to muddy the waters too much with super basic/dumb questions I have. I'll leave the final call on the request's origin to @letitz/security folks who presumably have done or are doing a more comprehensive review.

A random site could issue a fetch to the reporting origin, but the reporting origin needs to opt-in to later getting a report associated with that registration by setting the right response headers.

csharrison avatar Oct 12 '22 17:10 csharrison

@letitz's analysis makes sense to me. One more question - are the requests expected to be CORS-safe requests?

yoavweiss avatar Oct 24 '22 09:10 yoavweiss

@letitz's analysis makes sense to me. One more question - are the requests expected to be CORS-safe requests?

Discussed offline, but I don't think these are quite CORS-safe because they don't use form-data post body. You can see the spec text here: https://wicg.github.io/attribution-reporting-api/#create-report-request

I think the preflight is still not needed in this case though if we make these requests same-origin.

csharrison avatar Oct 24 '22 14:10 csharrison

@domfarolino and I chatted about this a while ago and unfortunately the chat was lost to time, but from my memory, I think the conclusion was because the flow described in https://github.com/WICG/attribution-reporting-api/pull/547#issuecomment-1276405044 also works across HTTP redirects (similar to Set-Cookie), this PR should use no-cors vs. setting the Origin header on resulting fetches for generated reports.

That is, a flow of:

  1. Fetch a.com on pub.com, which redirects (setting ARA headers) to b.com
  2. The b.com response sets ARA headers
  3. Later, we send reports to both a.com and b.com, because they both set relevant headers

@domfarolino can you confirm this matches your understanding? I'm not really an expert on the Fetch spec so it's hard for me to understand why this is the case. I didn't see anywhere specifying when this header is added (or its value) in the fetch spec.

csharrison avatar Mar 16 '23 18:03 csharrison

I have successfully paged every single piece of this issue out of my memory unfortunately.

I'm not really an expert on the Fetch spec so it's hard for me to understand why this is the case. I didn't see anywhere specifying when this header is added (or its value) in the fetch spec.

Can you clarify what behavior is confusing here? I'm not sure I follow.

domfarolino avatar Mar 20 '23 18:03 domfarolino

Can you clarify what behavior is confusing here? I'm not sure I follow.

My preference would be to land this PR, which sets the Origin header to the reporting endpoint's origin for ARA reports. The justification for this is that the reporting origin is the conceptual "initiator" of the resulting report (via specifying response headers configuring ARA, which triggers report generation). However, that is just a high level justification, it is not rooted in any spec text that refers specifically to rules dictating what the Origin header should be.

csharrison avatar Mar 20 '23 19:03 csharrison

I think that sounds fine to me, if it's OK with @letitz. One thing I want to revisit:

Given that the request is sent without credentials and we never inspect the response beyond the HTTP status code, what are the practical implications for this being CORS vs non-CORS?

Where in the spec is the status code observed? I don't see it happening around here: https://wicg.github.io/attribution-reporting-api/#ref-for-remove-a-report-from-the-cache%E2%91%A0.

domfarolino avatar Mar 31 '23 03:03 domfarolino

I think that sounds fine to me, if it's OK with @letitz. One thing I want to revisit:

From the security pov, I think a same-origin CORS request is fine. I also believe that's strongly preferrable to no-cors requests for modern features. @domfarolino if you give the Fetch spec thumbs up, I'm satisfied.

Given that the request is sent without credentials and we never inspect the response beyond the HTTP status code, what are the practical implications for this being CORS vs non-CORS?

Where in the spec is the status code observed? I don't see it happening around here: https://wicg.github.io/attribution-reporting-api/#ref-for-remove-a-report-from-the-cache%E2%91%A0.

Now I'm curious too :) I thought perhaps processResponse would only be invoked by fetch in success cases, but a quick glance at the fetch spec did not seem to support that idea.

letitz avatar Mar 31 '23 11:03 letitz

Thanks for re-requesting review. I'll be able to re-review once https://github.com/WICG/attribution-reporting-api/pull/547#pullrequestreview-1086163087 gets some discussion I think.

domfarolino avatar Apr 03 '23 13:04 domfarolino

Do we support this right now? If we allow that, then yes CORS is good, because if the request goes cross-origin we should be affirming CORS. Otherwise, we should use request mode same-origin since it is simpler, more honest, and errors-out if the reporting endpoint URL unexpectedly redirects.

I think we do support redirecting the request today. However, that might just be a quirk of our implementation. If the request redirects cross origin, the implication is we would only proceed if the redirect response contains Access-Control-Allow-Origin which allows the request origin? i.e. 5.4 of https://fetch.spec.whatwg.org/#http-fetch

csharrison avatar Apr 10 '23 21:04 csharrison

That's right, if you use the cors request mode. (For same-origin you get no chance to support cross-origin redirects). So I guess now is a good time to determine if the "quirk" in our implementation (following the redirect cross-origin) is something we want to memorialize here and stick with, or if we don't want to support cross-origin redirects even with CORS, and just use the same-origin request mode.

domfarolino avatar Apr 10 '23 23:04 domfarolino

IMO it'll be easier to get rid of the redirects, as I don't even think at our layer in the implementation (SimpleURLLoader) even supports CORS. Let's me circle back though.

csharrison avatar Apr 10 '23 23:04 csharrison