CSP frame-src blocks link download in Firefox but not Chrome
Categories
(Core :: DOM: Security, defect, P2)
Tracking
()
People
(Reporter: dveditz, Assigned: tschuster)
References
(Blocks 2 open bugs, )
Details
(Whiteboard: [domsecurity-backlog])
User Story
platform:windows,mac,linux impact:feature-broken affects:all
Attachments
(3 files)
In the MDN example of a link with the download attribute, the parent document has a CSP that does not include data: in its list of allowable frame-src sources. Inside the frame a download link has an href=data:image/png,... with a download="myfile.png" attribute. On Chrome this download works; in Firefox we block the load for violating the frame-src directive.
Without the download attribute it would be a navigation and both browsers would block it. With the download attribute Chrome recognizes the content isn't being loaded inside the frame and allows it, and Firefox seems to first check the CSP and block the load before realizing it's a download.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#result_6
The attached testcase is a simplified version of the broken part of the page. The document has a CSP with frame-src 'self'; which contains an iframe with 3 links.
- The first link ("download") is this compat bug, where Chrome allows it and Firefox doesn't
- The second link has target=_blank to open a new window, and neither browser stops that (once in the other window Chrome prevents it from loading for unrelated reasons)
- The third link tries to navigate to the Mozilla site which is not on on the frame-src list, so both browsers block it. Just here as a baseline check.
| Reporter | ||
Comment 1•2 years ago
|
||
If this turns out to be a Firefox bug we could move it to Core::DOM: Security. My expectation in this scenario is that Chrome is right: with the download attribute the content is NOT being loaded into the frame so CSP should leave it alone like it lets the target=_blank version go. Need to see if the spec is ambiguous or clear enough on this, and adding a WPT would be nice, too.
Comment 2•2 years ago
|
||
Verified this issue and it's still reproducible on Firefox versions 123 and 125.
Environment:
Operating system: Windows 10
Browsers: Firefox Nightly 125.0a1 (2024-02-20) / Firefox Release 123 / Chrome 121.0.6167.185
Note: Not reproducible on Chrome
Updated•2 years ago
|
Comment 3•2 years ago
|
||
Nominating for DOM:Security triage.
| Reporter | ||
Comment 4•2 years ago
|
||
Simon: does the Fetch spec specify when the download attribute is acted on in relation to when the CSP hooks are called?
- does the spec need clarifying?
- if it's clear, is Firefox wrong or Chrome?
- if Chrome turns out to be wrong, should we match them and change the spec anyway?
| Reporter | ||
Updated•2 years ago
|
Comment 5•2 years ago
|
||
The spec is more like Chrome as far as I can tell. The download is not a navigation.
Clicking a link is handled in https://html.spec.whatwg.org/#links-created-by-a-and-area-elements:activation-behaviour-2 which checks for download attribute, and step 6 calls https://html.spec.whatwg.org/#downloading-hyperlinks which doesn't do a CSP navigation check.
Comment 6•2 years ago
|
||
Anne pointed out that <a download> not using the navigate algorithm is a spec bug. See https://github.com/whatwg/html/issues/5548 , https://github.com/whatwg/html/issues/7718
Figuring out what we should do here might involve investigating what happens in Chromium and WebKit, and maybe fixing those spec bugs.
| Reporter | ||
Updated•2 years ago
|
Comment 7•2 years ago
|
||
If download is a navigation, surely it's always a top-level navigation? CSP doesn't control top-level navigation.
Comment 8•1 year ago
|
||
Is there any news about this? I regularely need to download stuff from an internal website of my org. Now I can't use Firefox because the security.csp.enable option from about:config was removed (which is good) but Firefox also doesn't seem to allow any other way to change the CSP on the client side (tried addons like "Modify Header Values" to no avail - which is bad).
So for now I'm stuck using Chrome.
Comment 9•1 year ago
|
||
Some additional info, in my case Firefox seems to block the download even though the CSP is set to allow it:
I login to the website https://example.com and get back the following CSP header:
Content-Security-Policy
default-src 'self'; script-src 'self' 'unsafe-inline'; frame-src ; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data:;
frame-ancestors 'self' https://.example.com https://example.com;
The site has an iframe with src="/memberArea"
In the iframe I click a button, some JS creates a new <a> and sets the download link, then clicks it. The download link is on a different domain and I get the following error:
Content-Security-Policy: The page’s settings blocked the loading of a resource (frame-src) at blob:https://abc.com/123 because it violates the following directive: “frame-src *”
But should frame-src * not permit the download?
Comment 10•1 year ago
|
||
Thank you, Benedikt, that's a good test case and useful context on top of what we already have. We should look at this again more closely.
Tom, is that something you can take a look at?
Comment 11•1 year ago
|
||
(In reply to Frederik Braun [:freddy] from comment #10)
Thank you, Benedikt, that's a good test case and useful context on top of what we already have. We should look at this again more closely.
Tom, is that something you can take a look at?
| Assignee | ||
Updated•1 year ago
|
| Assignee | ||
Comment 12•1 year ago
|
||
From what I remember in Firefox, the presence of the download attribute doesn't necessarily mean that the URL is going to be downloaded. From what I remember if a server uses Content-Disposition: inline it won't be downloaded. (We also restrict download to same-origin) The header obviously doesn't really apply blob: or data:, but unless we want to special case those, I think the point where we check the CSP might be too early to do a general fix.
Comment 13•1 year ago
|
||
Any news?
| Assignee | ||
Comment 14•1 year ago
|
||
(In reply to Benedikt Gansinger from comment #13)
Any news?
This is still on my Todo list, but low priority.
Do note: It sounds like you use the download attribute with a cross-origin URL, which doesn't work in Firefox even without CSP blocking the iframe.
Comment 15•1 year ago
|
||
IMO, unless a new download-src directive is added to to the CSP spec, it only makes sense for the (now removed) navigate-to directive to control navigation. Downloads are just navigation.
| Assignee | ||
Comment 16•1 year ago
|
||
For links the with the download attribute we call channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT) in nsDocShell::CreateAndConfigureRealChannelForLoadState. We don't seem to set this information anywhere on nsILoadInfo, so our CSP code doesn't know if something is a download. So we either need to add it somewhere, or even better from a CSP perspective: introduce a new nsContentPolicyType for (iframe) downloads.
I am not comfortable with just ignoring the CSP for DISPOSITION_ATTACHMENT channels, unless we make sure such channels can never end up displaying content. So far I haven't been able to find something like this. Nika, is this something we could do, or do you have an alternative suggestion?
Comment 17•1 year ago
|
||
(In reply to Tom Schuster from comment #16)
For links the with the
downloadattribute we callchannel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT)innsDocShell::CreateAndConfigureRealChannelForLoadState. We don't seem to set this information anywhere on nsILoadInfo, so our CSP code doesn't know if something is a download. So we either need to add it somewhere, or even better from a CSP perspective: introduce a newnsContentPolicyTypefor (iframe) downloads.I am not comfortable with just ignoring the CSP for
DISPOSITION_ATTACHMENTchannels, unless we make sure such channels can never end up displaying content. So far I haven't been able to find something like this. Nika, is this something we could do, or do you have an alternative suggestion?
Right now in Gecko, we basically consider a download attribute the same as if the network response had the Content-Disposition: attachment header specified. IIRC we don't have any distinction between the two cases which is tracked at the necko layer. IIRC other browsers do have more distinction between the two cases, which is perhaps where this is coming from?
I think this came up a bit a while back with browser.download.open_pdf_attachments_inline, which is something we'd need to consider if we made this change. IIRC if that pref is set we force pdfs to open inline even if they have an attachment disposition, so if we started ignoring CSP for download attribute pdf loads that might cause problems.
Not sure ottomh, but it might be possible to add TYPE_INTERNAL_DOCUMENT_DOWNLOAD and TYPE_INTERNAL_SUBDOCUMENT_DOWNLOAD internal content policy types for downloads. I expect changing all <a download> downloads to have a new external content policy type would be more risky. Unfortunately the decision about the content policy type for document loads is spread around a bit. In DocumentLoadListener the decision is made in CreateDocumentLoadInfo (https://searchfox.org/mozilla-central/rev/f256e50e068136275c1a2aff827aeddc92a75e07/netwerk/ipc/DocumentLoadListener.cpp#147-159), but we also make the decision for contentpolicy checks when opening a pop-up window in nsDocShell::PerformRetargeting (https://searchfox.org/mozilla-central/rev/f256e50e068136275c1a2aff827aeddc92a75e07/docshell/base/nsDocShell.cpp#8266-8271), and when starting a load in nsDocShell::DoURILoad (https://searchfox.org/mozilla-central/rev/f256e50e068136275c1a2aff827aeddc92a75e07/docshell/base/nsDocShell.cpp#10384-10393).
I expect the easier option would be to expose where the content disposition flag came from in HttpBaseChannel. We do know that information in the getter (the hint is stored separately from the header value: https://searchfox.org/mozilla-central/rev/f256e50e068136275c1a2aff827aeddc92a75e07/netwerk/protocol/http/HttpBaseChannel.cpp#706-717), so a helper function like GetContentDispositionHint or similar could give you the information.
| Assignee | ||
Comment 18•1 year ago
|
||
Thank you Nika!
I expect changing all <a download> downloads to have a new external content policy type would be more risky.
This was my feeling as well.
I expect the easier option would be to expose where the content disposition flag came from in HttpBaseChannel.
Good idea. We just need to carry this bit over to nsILoadInfo as well, so that we can access it in the CSPService::ShouldLoad, which doesn't get a channel.
| Assignee | ||
Comment 19•1 year ago
|
||
| Assignee | ||
Comment 20•1 year ago
•
|
||
Well there is a bit of an ordering issue that I am not sure yet how to resolve.
- We create the nsILoadInfo (i.e. in
CreateDocumentLoadInfo) - We create the channel using the
nsILoadInfo(innsDocShell::CreateAndConfigureRealChannelForLoadState) - We call
SetContentDispositionon the created channel to force a download, which can fail (we can't modify nsILoadInfo anymore, I think?) - We end up calling
AsynOpen - We call
CSPService::ShouldLoadwith just thensILoadInfo(viansContentSecurityManager::doContentSecurityCheck)
I see two solutions:
What I implemented in my patch above: We set a flag during on nsILoadInfo in step 1., which might be set even for channels which don't support SetContentDisposition. I think this might not be a real problem, because as far as I can tell all channels that can be displayed inside an iframe also support this call. And external protocols don't go through CSP anyway.
We could try passing nsIChannel to nsIContentPolicy::ShouldLoad (which CSPService implements), but that means kind of breaking the contract of NS_CheckContentLoadPolicy, which does not necessarily require a real channel. I guess a similar solution would be to skip calling into from DoContentSecurityChecks, but that also feels foul.
Description
•