Page MenuHomePhabricator

RFC: Partial opt-out method for Content security policy
Open, MediumPublic

Description

  • Affected components: MediaWiki core and its CSP feature.
  • Initial implementation: TBD.
  • Code stewards: TBD.

Motivation

RFC T135963 has provided the fundamentals of the CSP infrastructure in MediaWiki core. We can configure the CSP header, toggle whether to report-only or enforce, and send/receive violations via a new API module, and more.

Originally I had planned to write an additional public RFC on the user opt-out method of Content Security Policy on Wikimedia. Since the original RFC, a number of things have changed:

  • A re-focus to first focus on forbidding external scripts, rather than concentrating on the anti-XSS provisions of CSP
  • A recent string of account compromises using external scripts has caused security team to expedite deploying CSP in report only mode in order to gain logging insight into external scripts that are being loaded.

With that in mind, the new threat model we are trying to address in the near-term with CSP is:

  • Privacy protection: Prevent both malicious and accidental privacy leaks by preventing loading third-party resources (e.g. Prevent the helpful interface-admin who doesn't realize the implications of putting google analytics into the site JS)
  • Force people who attack our sites with malicious javascript (e.g. by taking over an interface-admin account and editing MediaWiki:Common.js) to place all malicious JS on-site so that we can determine exactly what the attacker was doing after the fact.

We believe concentrating solely on preventing third party resource usage will be a much easier transition for the community and also provide significant immediate benefits in terms of security and privacy. We are still very much interested in the anti-XSS measures of CSP (e.g. banning 'unsafe-inline'), but intend to leave that to the distant future for now.

There's also some on-wiki notes about this at https://www.mediawiki.org/wiki/User:BWolff_(WMF)/CSP_plan

Requirements

Before we can go fully into enforce mode for all users, we need to address the needs of gadgets that communicate with external APIs. This is a common use case on wikis today. Often this is Cloud VPS/Toolforge (See counter vandalism network. There are several others). While we intend to fully ban loading external scripts, allowing external API access to send/receive data on an opt-in basis is a totally reasonable thing for users to want.

  • A gadget can only load data from external APIs that the user opted-in for.
  • User opt-in must be on a per-origin basis.
  • The allowance is only for sending and receiving data. Not for importing executable JavaScript code.
  • The allowances need to be stored somewhere per-user.
  • The core CSP mechanism needs to read these out and add the necessary exemptions to the CSP header on each relevant page view.

Exploration

I propose adding a special page which allows users to add a list of domains to include in the default-src directive of their CSP rule. The reason not to use a user preference is that I think changing this value should require re-authentication, as adjusting it would be useful to a malicious person.

This would only modify default-src and not script-src. So people still would only be able to directly load wikimedia sources via importScript() and friends. However as we are not prepared at this time to disable eval() and variants, people could still get around this if they wanted to. I hope banning external JS in script-src will at least discourage this practise, and perhaps in some glorious future we will be able to get rid of unsafe-eval and unsafe-inline. That is undoubtedly a long way off.

This will be backed by a custom DB table that lives in the centralauth DB (Thus it will be a global option).

The schema for this table will be something like:

CREATE TABLE /*_*/csp_sources (
	csp_user unsigned int NOT NULL,
	csp_domain VARBINARY(255) NOT NULL,
	csp_timestamp BINARY(14) NOT NULL
);

There's some question as to what the allowed values should be. Originally I envisioned this as a simple checkbox that would add * to the default-src value, allowing access to the entire internet. Upon further reflection, I think we should discourage users from doing that. Forcing users to input specific domains reduces the potential risk to the specific domains they actually use. It also means it is less likely for a wide number of users to all use the same domain, meaning an attacker won't be able to just compromise a single domain to target all the opted-out users. Thus I would like to require users to input specific domains, and furthermore in the case of Cloud VPS they wouldn't be allowed to specify *.wmflabs.org but instead must specify someproject.wmflabs.org. In the case of toolforge, they would have to specify a path, so tools.wmflabs.org/bawolff/ and not simply tools.wmflabs.org.

Thanks, I appreciate feedback on this proposal.

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

In general, there shouldn't be scripts in the site's common.js that are hitting external APIs. If such scripts are needed for some reason, the necessary domains could be added to $wgCSPHeader after being evaluated by the Security team.

In general, there shouldn't be scripts in the site's common.js that are hitting external APIs. If such scripts are needed for some reason, the necessary domains could be added to $wgCSPHeader after being evaluated by the Security team.

With that in mind, any chance of, if only temporary, adding the domains xtools.wmflabs.org and archive.org to that variable? A widely used gadget is being blocked from working correctly because of this.

I don't think we're enforcing CSP anywhere yet, so it shouldn't be blocking anything from working.

It is being enforced. I have the console errors to go with it.

[Error] [Report Only] Refused to connect to https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island because it appears in neither the connect-src directive nor the default-src directive of the Content Security Policy.
[Error] Origin https://en.wikipedia.org is not allowed by Access-Control-Allow-Origin.
[Error] XMLHttpRequest cannot load https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island due to access control checks.
[Log] The requested service https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island failed: 0, error (index.php, line 160)
[Error] Failed to load resource: Origin https://en.wikipedia.org is not allowed by Access-Control-Allow-Origin. (books, line 0)

As suggested by [Report Only], that is only a report, not a real error. The real errors have to do with CORS, not CSP (and the restriction is on the archive.org side).

[Error] [Report Only] Refused to connect to https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island because it appears in neither the connect-src directive nor the default-src directive of the Content Security Policy.

This is from CSP. See how it says "Report Only", that means it didn't block, just reported.

[Error] Origin https://en.wikipedia.org is not allowed by Access-Control-Allow-Origin.

This says that the response from the remote server did not allow CORS.

When I try making a CORS request to your URL, it seems to have ignored the Origin header and issued a non-CORS response.

[Error] XMLHttpRequest cannot load https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island due to access control checks.
[Log] The requested service https://archive.org/services/context/books?url=https://en.wikipedia.org/wiki/Easter_Island failed: 0, error (index.php, line 160)
[Error] Failed to load resource: Origin https://en.wikipedia.org is not allowed by Access-Control-Allow-Origin. (books, line 0)

All that is because of the CORS failure, not CSP.

In general, there shouldn't be scripts in the site's common.js that are hitting external APIs. If such scripts are needed for some reason, the necessary domains could be added to $wgCSPHeader after being evaluated by the Security team.

With that in mind, any chance of, if only temporary, adding the domains xtools.wmflabs.org and archive.org to that variable? A widely used gadget is being blocked from working correctly because of this.

Looks like you somehow reposted T208188#4795556? It was already answered above.

Looks like you somehow reposted T208188#4795556? It was already answered above.

Oops I did. It is indeed a CORS issue, but it still would be fantastic to have those domains Whitelisted before CSP gets enforced. :-)

One of the things being discussed in here is a way for gadgets to easily prompt users to add the necessary CSP opt-outs.

One of the things being discussed in here is a way for gadgets to easily prompt users to add the necessary CSP opt-outs.

We should make sure this behaves correctly for gadgets that are enabled per default (including for anon users). "Correctly" here means "no opt outs for default gadgets".

It seems to me that we will need a global blacklist, so ops can effectively override user opt-in on this, e.g. when a given source is compromised.

I just realized this this is misleading:

csp_domain VARBINARY(255) NOT NULL

We want URL prefixes (or full URLs), not domains. So this should be:

csp_url VARBINARY(255) NOT NULL

I just had a brief chat with @hoo, and he noted that we'll have to protect the special page against gadgets that try to exempt themselves via that special page. And of course against external attackers that try to force exemptions via XRF. The very minimum is POST-only and a token, but we may have to force re-authentication. Also, we may want to suppress the loading of gadgets and user scripts on that page, like we do on login and preferences pages.

Another option is to not do this work. I don't see a good justification for letting any users bypass CSP. The ones who will complain the most about this are also the ones who shouldn't be allowed (sysops, interface admins, stewards, …). Yes, toolforge has a number of neat tools, but this is proposing a lot of fiddly work which I don't think adds value.

If the demand is really high, a much simpler alternative would be to have a "blessed scripts" repo that lets proxy scripts for toolforge tools be made available after manual security review on each patch, and which is exposed via docroot from mediawiki.org/magic or whatever.

If the demand is really high, a much simpler alternative would be to have a "blessed scripts" repo that lets proxy scripts for toolforge tools be made available after manual security review on each patch, and which is exposed via docroot from mediawiki.org/magic or whatever.

Let's not do that, we are already nowhere near able to deal with code review requests, even without having to deal with users who don't speak English and scripts which only make sense in the context of some workflow that only exists on the Zulu Wikisource. Also reviewers tend to force fairly arbitrary and diverse code style requirements on people (which is reasonable when done for a repo with shared maintainership, not so reasonable when the reviewer has no stake in the code ever running, and I'm not sure we could avoid still doing it in that situation).

Another option is to not do this work. I don't see a good justification for letting any users bypass CSP.

Making it effectively impossible to call to ToolForge from gadgets seriously diminishes the usefulness of ToolForge.

Another option is to not do this work. I don't see a good justification for letting any users bypass CSP.

Making it effectively impossible to call to ToolForge from gadgets seriously diminishes the usefulness of ToolForge.

Nothing stops a ToolForge-based tool wrapping a Wikimedia wiki page or set of API call; it's just mildly less "cool" for the users to use the tool from tools.wmflabs.org/foo rather than de.wiktionary.org.

One thing I would consider is adding some kind of usage tracking (I think we can do that by having a stricter report-only and a looser full one and including the user id in the report uri) so people can see which URLs they are actually using and which can be removed. Otherwise they will come back after a year and have no clue which domain is for what (assuming we don't limit them to toolforge, those are least reasonably easy to interpret) and so they'll keep collecting dust forever even when the tool is defunct (users rarely remove gadgets that stop working) and will become ripe targets for attack.

However as we are not prepared at this time to disable eval() and variants, people could still get around this if they wanted to.

Or even if they did not want to, if they use something like $.getScript without realizing that that circumvents the CSP policy.

assuming full lookup of user groups on all wikis too expensive on every request

I don't see why it would be, it's easily cacheable.

Another suggestion that ive heard is have some sort of friendlier interstitial similar to oauth that people can agree to.

I was just about to suggest that, too. For gadgets it could be part of the enabling process (we might want to move away from the current system of gadget management happening on a slightly customized preferences tabs, although it's not strictly necessary). For user scripts (and as Anomie said above, it's not really realistic to do away with user scripts, short of something like T36958: User-level gadget repositories) we could use SecurityPolicyViolationEvent to trigger the interstitial (sorry Internet Explorer users) or provide an mw.csp.authorize().

(from the IRC meeting)

<duesen> I have a question about transitioning: if I'm usinx gadget X, which did not used to require access to an external resource, but is changed to now require it, what happens? does it break? is it disabled, and I have to re-enable it? how does this happen? how do I notice?

Whenever the CSP whitelist is changed, the gadget gets temporarily disabled for everyone, and they get an echo notification about it (which doesn't go away until you reenable/fully disable)? Would require a third state for gadgets besides enabled / not used, which is probably not hard to do as an ugly hack on top of the existing ugly hack of enabling gadgets via user options. We'd probably want some warnings in the gadget preferences UI, but it is well past time to turn that UI into something more BetaFeatures-like, anyway.

<_joe_> so you can authorize github.com/<someone> if you trust them

(and then several other people commenting about github user paths)

This part of the IRC discussion left me a bit confused. There is nothing interesting a script can do with github.com/<someone>. Either you want to run remote scripts and then it's raw.githubusercontent.com/<someone>, but I thought the plan is to ban all external script loading period (I certainly don't see any use case for allowing it). Or you want the github api, which is api.github.com, paths are generally not prefixed with the username, and anyway what would be the point of limiting GitHub API access to certain repos?

From last week's TechCom meeting:

The current proposal of extending the CSP policy with a dynamically determined list of exemptions from a new database table, managed by the user via a special page seems solid and we found no concerns with this approach.

However, we've been hesitant to put this on Last Call because we want to first make sure that this would suffice from a product perspective. That isn't a decision we can make, and if this has been approved already then a brief note indicating that would suffice. If not, then I think a bit more research and iteration might be required before we can proceed. In particular, because that research could lead you to prefer a different approach.

Two use cases in particular that I (personally) think may need to be considered are:

  • Gadgets - This one is probably fairly straight-forward. The gadgets definition format is easy to extend, and the preferences page provides an existing secure venue for an interstitial to ask the user's consent.
  • User scripts - This one is tricky. Depending on how we tackle this, it's quite likely to involve significant changes in many areas of MediaWiki that I believe should be discussed in scope of this RFC.

@Bawolff For user scripts, see T208188#4730946 and T208188#4732305 about how exemptions might be added/requested. There's also the story of how/if they would be removed after scripts change internally or are uninstalled.

@Bawolff I see no one have commented here in the last weeks. CSP is enabled for ~3 months and there are important privacy concerns with current situation (T207900#4846582).
Would you mind closing the RFC and breaking this to tasks based on what was agreed here?

This is summary of the above discussion as far as I understand

Core requirements

There is more or less agreement about the requirement from core in sense of DB/UI:

  • Define exemptions table
CREATE TABLE /*_*/csp_sources (
	csp_user unsigned int NOT NULL,
	csp_url VARBINARY(255) NOT NULL, /* not csp_domain ; per Daniel's comment */
	csp_timestamp BINARY(14) NOT NULL
);
  • Define user interface to enable it

Suggested methods to populate it

Regarding population of this table the following methods were suggested:

Methodproscons
SecurityPolicyViolationEventBrowsers builtin feature [covers gadgets/userscripts etc]Experimental, not supported by IE, non intuitive mapping between feature (gadgetX) and resource (http://...)
gadget-definitionEasy to define and trackonly gadgets [not user scripts]
mw.csp.authorize / mw.csp.requestExceptionWould work for gadgets/user scriptsImperative approach (non declarative) requires change in all user scripts
magic comments (/* @trust https://... )can work for user scriptscomplex and fragile

I think we should go for degrade gracefully approach mixing the above solutions:

MW Core:

  1. Core defines CSP table and simple UI though special page (solution for any MW instance, whether or not gadgets extension is installed; works in IE)
  2. SecurityPolicyViolationEvent to popup oauth like dialog for ease of use (no need to access to special page). E.g: document.addEventListener("securitypolicyviolation", (e) => { mw.csp.requestException(featureName=NULL, resources=[e.blockedURI]) }); // "Some unknown script have tried to load external code X. This may pose a security/privacy risk. [Block] [Approve]")

Aside from core solution we can OPTIONALLY (recommended but not required) expose it in a user friendly whenever we want to modify it. lazy gadget/userjs developers who don't define trusted urls gives the core to handle it in less user friendly solution (no feature name).

Extension Gadget:

  • Add to gadget-defintions trusted URLs
  • Expose the core capabilities: in Gadget section whenever enabling gadget:

onGadgetInstall(gadget) => { mw.csp.requestException(featureName=mw.msg(gadget.name), resources=gadget.trustedUrls) } // "Gadget-X would like to allow to load external resource X. This may pose a security/privacy risk. [Block] [Approve]") }

User scripts:

  • Optionally implement

mw.csp.requestException(featureName='MY USER SCRIPT', resources=myTrustedUrls) } } // "MY USER SCRIPT would like to allow to load external resource X. This may pose a security/privacy risk. [Block] [Approve]")

sbassett triaged this task as Medium priority.Sep 23 2019, 4:36 PM

@Bawolff I see no one have commented here in the last weeks. CSP is enabled for ~3 months and there are important privacy concerns with current situation (T207900#4846582).
Would you mind closing the RFC and breaking this to tasks based on what was agreed here?

Just as an update, I am now working on this in earnest again.

Regarding UI for populating the table, the options seem to be:

  • magic comments (/* @trust https://... ) - I'm opposed to this. I think this is too fragile and would involve a lot of complication in parsing these details out. Not to mention, when would the auth flow happen? When the user makes an edit to Special:MyPage/common.js ? How does cross-wiki includes work in this scheme
  • gadget-definition - I think this is my favored approach for gadgets.
  • SecurityPolicyViolationEvent - I'm not a fan. I worry that this will turn into "automatically say yes without reading". I would prefer that script writers have to explicitly declare external resource usage, so they don't just do it accidentally
  • mw.csp.authorize / mw.csp.requestException - yeah I think this would be ok. My only concern is that a flow based on imperative ask for permissions might cause it to happen far away from when the user added the script to their user js, and thus they might not correlate the prompt with which script caused it.
  • Special page that users manually add stuff to - I think this might suffice for the initial rollout, as a minimal viable product. Keep in mind that user scripts are an advanced feature that already require much technical knowledge to use. Furthermore, most user scripts do not load external data.

Another question: is the exception table global or perwiki? I think global makes the most sense

An additional concern - ideally, a malicious script looking to either exfiltrate data [To be clear, fully preventing exfiltration is basically impossible, and out of scope. CSP will prevent the obvious methods though] or receive instructions from an external source, should not be able to simply adjust the allow list. Otherwise there wouldn't be much point to having a whitelist. To that end, I would strongly like that adding an entry to the allow list requires the user to re-authenticate, similar to changing an email. This is one of my main motivations for proposing a separate table, instead of just adding a preference.

CREATE TABLE /*_*/csp_sources (
	csp_user unsigned int NOT NULL,
	csp_url VARBINARY(255) NOT NULL, /* not csp_domain ; per Daniel's comment */
	csp_timestamp BINARY(14) NOT NULL
);

Not sure if it was discussed above, but from a security/privacy perspective, it probably makes sense to redact csp_url (or whatever it ends up being) from dumps, etc. so as not to expose those specific resources to potential attackers. In fact, it might make sense to go even further and redact the entire table since I'm not sure how much informational value it has other than to potential attackers. (Not that you can't find similar info just searching users' common.js, etc, this would just be a much easier way to do so.)

CREATE TABLE /*_*/csp_sources (
	csp_user unsigned int NOT NULL,
	csp_url VARBINARY(255) NOT NULL, /* not csp_domain ; per Daniel's comment */
	csp_timestamp BINARY(14) NOT NULL
);

Not sure if it was discussed above, but from a security/privacy perspective, it probably makes sense to redact csp_url (or whatever it ends up being) from dumps, etc. so as not to expose those specific resources to potential attackers. In fact, it might make sense to go even further and redact the entire table since I'm not sure how much informational value it has other than to potential attackers.

I agree - even without the attack scenario, its basically a glorified user preference, and we treat other user preferences (with minor exceptions) as private user data; i think we should do the same here

Krinkle renamed this task from Proposal for partial opt-out method for Content security policy to RFC: Partial opt-out method for Content security policy.Nov 27 2019, 9:26 PM

Just as an update, I am now working on this in earnest again.

Thanks for the update.

Regarding UI for populating the table, the options seem to be:

  • magic comments (/* @trust https://... ) - I'm opposed to this. I think this is too fragile and would involve a lot of complication in parsing these details out. Not to mention, when would the auth flow happen? When the user makes an edit to Special:MyPage/common.js ? How does cross-wiki includes work in this scheme
  • gadget-definition - I think this is my favored approach for gadgets.
  • SecurityPolicyViolationEvent - I'm not a fan. I worry that this will turn into "automatically say yes without reading". I would prefer that script writers have to explicitly declare external resource usage, so they don't just do it accidentally
  • mw.csp.authorize / mw.csp.requestException - yeah I think this would be ok. My only concern is that a flow based on imperative ask for permissions might cause it to happen far away from when the user added the script to their user js, and thus they might not correlate the prompt with which script caused it.
  • Special page that users manually add stuff to - I think this might suffice for the initial rollout, as a minimal viable product. Keep in mind that user scripts are an advanced feature that already require much technical knowledge to use. Furthermore, most user scripts do not load external data.

Agree, I like the KISS approach for special page for initial rollout

Another question: is the exception table global or perwiki? I think global makes the most sense

Global exception table makes more sense IMO. Also in case of a breach it will be slightly easier to invalidate all entries with specific csp_url in 1 query.

Change 557581 had a related patch set uploaded (by Brian Wolff; owner: Brian Wolff):
[mediawiki/core@master] [WIP] Special page to add exceptions to CSP policy

https://gerrit.wikimedia.org/r/557581

Should it be possible to add a short comment to the exceptions? Otherwise, users will ask themselves why they added an exception several years ago and wonder if they still use it. So I think it would make sense to allow adding a short comment like "exception for script foo" along with the exception.

Question: Why is this being done in a separate table rather than as a user preference (even if it is set using a dedicated special page instead of via preferences)? I.e., why not have the new special page, after ensuring authentication, etc. save a preference?

Question: Why is this being done in a separate table rather than as a user preference (even if it is set using a dedicated special page instead of via preferences)? I.e., why not have the new special page, after ensuring authentication, etc. save a preference?

To make sure it is global and not local, and to ensure that it can't be set via other apis. I suppose both are at least in theory possible to do via user_options but it seemed like a separate table was cleaner.

Note: My patch is now much more complete and awaiting feedback :)

Tagging Platform Engineering to keep in the loop, and possible provide code review.

So the final table structure is

CREATE TABLE /*_*/csp_exceptions (
    cspe_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
    -- The global user id. user.user_id in default config. globaluser.gu_id with CentralAuth
    cspe_user int unsigned NOT NULL,
    -- Anything that can be a CSP source. Typically a domain name
    cspe_src varbinary(255) NOT NULL,
    -- foreign key to comment table
    cspe_comment_id bigint unsigned NOT NULL,
    -- Insert timestamp
    cspe_timestamp binary(14)
) /*$wgDBTableOptions*/;

CREATE UNIQUE INDEX /*i*/cspe_user_src ON /*_*/csp_exceptions (cspe_user, cspe_src);

With the idea that the table is global and lives (only) on meta. Some things to consider with this design:

  • General scaling assumptions: Most users have 0 entries in this table. A small percentage of power users will have probably around 5 entries in this table. There is a system enforced limit of no more than 40 entries per user.
  • Basically all logged in requests for all (SUL) wikis now depend on the meta db. There's a layer of caching in memcache, so its not hit on literally every request, but nonetheless it depends on it.
  • cspe_src is likely to be somewhat repetitive. If we were really worried about duplication we could normalize it to a different table
  • This uses the comment table.
  • This means that there is cross-wiki writes to the comment table on meta. This should be fine, but I don't think anything else does that, and it would be nice if someone who worked on the comment system could confirm that's ok

That should be fine.

@Bawolff I've updated the task description to match the new rubric. I filled out the requirements based on what I gathered from the rest of the description. Feel free to adjust or correct as you see fit. The idea is that this makes it easier for people to contribute to the proposal by separating what is needed from what the current proposal does.

Regarding UI for populating the table, the options seem to be:

  • magic comments (/* @trust https://...) - I'm opposed to this. I think this is too fragile and would involve a lot of complication in parsing these details out. Not to mention, when would the auth flow happen? When the user makes an edit to Special:MyPage/common.js ? How does cross-wiki includes work in this scheme
  • gadget-definition - I think this is my favored approach for gadgets.
  • SecurityPolicyViolationEvent - I'm not a fan. I worry that this will turn into "automatically say yes without reading". I would prefer that script writers have to explicitly declare external resource usage, so they don't just do it accidentally
  • mw.csp.authorize / mw.csp.requestException - yeah I think this would be ok. My only concern is that a flow based on imperative ask for permissions might cause it to happen far away from when the user added the script to their user js, and thus they might not correlate the prompt with which script caused it.
  • Special page that users manually add stuff to - I think this might suffice for the initial rollout, as a minimal viable product. Keep in mind that user scripts are an advanced feature that already require much technical knowledge to use. Furthermore, most user scripts do not load external data.

Another question: is the exception table global or perwiki? I think global makes the most sense

My concern with the special page approach is that it means there is no integration between CSP and scripts/gadgets whatsoever. I fear that this would greatly harm the gadget ecosystem and also not set us up for success in terms of security.

Some scenarios to consider:

  • I enable a checkbox on Special:Preferences/gadgets. The gadget reads data from an API. Is the gadget going to fail half-way through? How will it break? What is the user experience like post-enforce for a typical gadget as it exists today? What can a gadget developer do to make sure their users don't feel the gadget is broken? What can we do to make sure it doesn't feel like MW is broken?
  • I have somehow enabled a gadget and whitelisted its API endpoint. The gadget developer is now changing or adding an API endpoint. The same questions apply. How do we make this an experience that doesn't lead to "It's broken, I don't know what to do".
  • A gadget I have enabled has discontinued one of their API endpoints. Perhaps they no longer need an extenal API. Perhaps they no longer trust this API. Perhaps they have consolidated two API endpoints into one. How do we make sure here that the extenral API can't be hijacked (e.g. after the domain expires) to be able to be abused by users who still have this old thing whitelisted for a gadget that doesn't ask for it anymore?
  • I used to have a gadget enabled and I whitelisted an external API for it. Let's say the gadget is specific to an external party (e.g. "Translate something with Bing"), and I disable the gadget because I no longer trust this party.

To clarify, I don't think we need to have all of these addressed with an implementation upfront. But I'd like to hear:

  • What is the rough plan for these?
  • Do we know if those can be addressed in a good way without making breaking changes to the current proposal? (e.g. we may need to add a way to track what entity an website was whitelisted for).
  • If you believe it would require breaking changes, is there a reasonable path forward? E.g. would you be okay with having all entries wiped away once we have a way to tracking stuff per-script, so that we retroactively ensure any old stuff won't remain forever? If not, it'd help gain confidence in the current draft if there is some other (even vague) plan for how to reconcile this. For example, is adding things manually a feature requirement we want to have long-term, or is it a temporary implementation detail until we can track things specifically?
  • Is the PHP side of interacting with the table meant to be a public API within MediaWiki code, or can we consider it entirely private to the CSP component? E.g. would it have hooks or be allowed to be called from extensions.

Please note, my contract with WMF ended, so I'm not really working on this anymore. The security team will presumably be detailing what the future plans are here. However, I wanted to mention what my thoughts were on this subject at the time when I left.

I enable a checkbox on Special:Preferences/gadgets. The gadget reads data from an API. Is the gadget going to fail half-way through? How will it break? What is the user experience like post-enforce for a typical gadget as it exists today? What can a gadget developer do to make sure their users don't feel the gadget is broken? What can we do to make sure it doesn't feel like MW is broken?

My long-term plan was that you could specify additional hosts needed in MediaWiki:gadgets-definition. There would be a prompt when enabling the gadget as to whether you are ok with accessing that resource. In the short term though, I was expecting the special page to suffice.

Do we know if those can be addressed in a good way without making breaking changes to the current proposal? (e.g. we may need to add a way to track what entity an website was whitelisted for).

I was kind of expecting that any tracking needed per gadget, would be a separate system than the per-user exceptions (on the backend). I'm not sure it makes sense to combine the two, beyond having the Special:ContentSecurityPolicyExceptions UI display both.

If you believe it would require breaking changes, is there a reasonable path forward? E.g. would you be okay with having all entries wiped away once we have a way to tracking stuff per-script, so that we retroactively ensure any old stuff won't remain forever? If not, it'd help gain confidence in the current draft if there is some other (even vague) plan for how to reconcile this. For example, is adding things manually a feature requirement we want to have long-term, or is it a temporary implementation detail until we can track things specifically?

I was thinking that a general per-user opt out would be needed regardless of anything done with gadgets. I don't think user-scripts are compartmentalized enough to effectively track them per script, so I was thinking per-script tracking (when/if it exists) would only apply to gadgets, and any non-gadget user-scripts would continue to require users to manually adjust Special:ContentSecurityPolicyExceptions.

Is the PHP side of interacting with the table meant to be a public API within MediaWiki code, or can we consider it entirely private to the CSP component? E.g. would it have hooks or be allowed to be called from extensions.

I was thinking private for the exception table. There are other hooks in the CSP system for extensions to interact with if they want to adjust the CSP policy.

I was thinking that a general per-user opt out would be needed regardless of anything done with gadgets. I don't think user-scripts are compartmentalized enough to effectively track them per script, so I was thinking per-script tracking (when/if it exists) would only apply to gadgets, and any non-gadget user-scripts would continue to require users to manually adjust Special:ContentSecurityPolicyExceptions.

For the record, MCR could be used to attach information about required external domain to user scripts.
This would make management and re-use of user scripts easier. OTOH, that may not be something we want to encourage.

Please note, my contract with WMF ended, so I'm not really working on this anymore. The security team will presumably be detailing what the future plans are here. However, I wanted to mention what my thoughts were on this subject at the time when I left.

In context of the RFC process, the question is whether the proposed technical solution is satisfactory and feasible in the context of current and anticipated future requirements. The problem seems to be that such requirements are unclear, since there is no clarity on what would be acceptable or desirable behavior from the user's perspective, in the short and medium term.

It seems clear that we can't just cut off access to external domains with no replacement. Whoever makes that determination would also have to determine what we should be doing instead. If the need for changing the status quo is driven by security considerations, the security team should drive for a decision on this from the group responsible for the user experience.

In context of the RFC process, the question is whether the proposed technical solution is satisfactory and feasible in the context of current and anticipated future requirements. The problem seems to be that such requirements are unclear, since there is no clarity on what would be acceptable or desirable behavior from the user's perspective, in the short and medium term.

I've had one off conversations with some community members at various events. They seem to be ok with this solution, but it is a very small sample size.

It seems clear that we can't just cut off access to external domains with no replacement.

Indeed, nobody is proposing that, and there is code written and awaiting review to implement the special page solution, which was discussed during the RFC meeting from dec 2018.

In response to the task description: I think it's a bit much to require re-authorization to add a domain. Or more specifically, I think it's very reasonable for unknown domains. I think it's very wise to require authorization to load http://1901529060.xyz/session.js. But 1901529060.xyz is not quite the same as Flickr. The Flickr import in UploadWizard already warns the user: "This form will load content hosted by flickr.com and subject to the Flickr terms of use and privacy policy. Please note that by using this tool, your IP address and request details will be available to Flickr."

If you must a one-time checkbox could be added to that which would remain checked for future uploads if accepted once. Either way re-authorization should not be required here.

Of course, Flickr already knows your IP. How did you obtain a Flickr link to import to begin with? Exactly.

@AlexisJazz What you describe (one-time permission for a specific third party, stored with your user account indefinitely) is exactly what is proposed and being built.

@AlexisJazz What you describe (one-time permission for a specific third party, stored with your user account indefinitely) is exactly what is proposed and being built.

Right, what I'm saying is that users shouldn't have to edit a special page to allow Flickr. If users can tick a checkbox in the UploadWizard and that stores the exception for Flickr magically on that special page without leading the user away from the UploadWizard, that's fine.

This RFC is about gadgets and other user-generated scripts which are not reviewed and verified prior to being made available to users. For those, we need users to consent in a standardised way as the scripts themselves cannot be assumed or guruanteed to do this.

The foundation's privacy policy and general user expectations of privacy apply regardless of what technical solutions exist. Right now, UploadWizard already satisfies policy as it does not commnicate with Flickr from the user's browser unless the user interacts with that part of the feature, and even then only after it has warned the user about this. UploadWizard is installed on the server and can dynamnically add this third-party domain as needed to the CSP config for a specific special page. This already works today and does not need to change in any way. The system described in this RFC does not apply there.

This RFC is about gadgets and other user-generated scripts which are not reviewed and verified prior to being made available to users. For those, we need users to consent in a standardised way as the scripts themselves cannot be assumed or guruanteed to do this.

The foundation's privacy policy and general user expectations of privacy apply regardless of what technical solutions exist. Right now, UploadWizard already satisfies policy as it does not commnicate with Flickr from the user's browser unless the user interacts with that part of the feature, and even then only after it has warned the user about this. UploadWizard is installed on the server and can dynamnically add this third-party domain as needed to the CSP config for a specific special page. This already works today and does not need to change in any way. The system described in this RFC does not apply there.

Thanks for the clarification. What do you mean by "this already works today"? It doesn't work on betacommons.

What do you mean by "this already works today"? It doesn't work on betacommons.

See T244124

What do you mean by "this already works today"? It doesn't work on betacommons.

See T244124

I don't get it.

Content Security Policy: The page’s settings blocked the loading of a resource at https://api.flickr.com/services/rest/?&api_key=e9d…=json&nojsoncallback=1&method=flickr.photos.licenses.getInfo (“default-src”).

I don't get it.

Content Security Policy: The page’s settings blocked the loading of a resource at https://api.flickr.com/services/rest/?&api_key=e9d…=json&nojsoncallback=1&method=flickr.photos.licenses.getInfo (“default-src”).

That’s why there is T249486: Change Content Security Policy on betacommons to allow api.flickr.com as well. But it has nothing to do with this ticket.