Problem/Motivation
See #3035979: [DISCUSSION] Should a facility be added to JSON:API which limits automatic exposure of all entities for prior related discussion.
In #3035979-14: [DISCUSSION] Should a facility be added to JSON:API which limits automatic exposure of all entities, @greggles said:
[Drupal 8's HTTP APIs are] shining light on some code paths that are not as well hardened as the rest of Drupal. As a community of developers and site builders our sophistication around writing and testing these kinds of sites is advancing.
In other words, if the "First" in API-First means making APIs a first-class citizen (i.e., making everything a site can do via HTML also accessible via APIs), then that's a great design principle and goal. However, we also need to be aware of Drupal's actual history, where HTML came "first" (chronologically), and it's taking time for Drupal contrib and custom module developers to adjust to the security implications surfaced by HTTP APIs.
For example:
- One can write an entity type for which there is no HTML route at which it can be viewed independently of being edited (this is common for config entity types, but can also include content entity types). And in the process of doing so, one can accidentally introduce an access control handler that allows 'view' permission to someone who does not have 'edit' permission. And not realize this until someone is able to get that entity data via an HTTP API, that they can't get via HTML.
- The above can also happen for individual fields within a given entity type or within any entity type.
- One can write an entity type or a field whose HTML form (via Drupal's Form API) performs critical input validation that isn't performed by the entity type's or fields' validation constraints or serialization denormalizers. And again, not realize this until someone is able to corrupt the data and/or exploit something via that corrupted data, in a way that they can't do via the HTML form.
Ideally, all module developers would know about all these pitfalls, and code all of their entity types, field types, validation constraints, normalizers, and access control handlers, correctly and securely. And also, have as much automated testing and perform as much manual testing, for HTTP API usage of their module's features, as they do for the HTML usage. But, we're not there yet.
So, let's figure out what, if any, additional safeguards and mitigations could be added without harming the DX and UX for people working with Drupal for API-First projects.
Proposed resolution
Several ideas were proposed throughout #3035979: [DISCUSSION] Should a facility be added to JSON:API which limits automatic exposure of all entities. Let's add child issues for ones we want to explore further.
Remaining tasks
Issues yet to be filed:
- A simple status report message indicating JSON:API's read/write status (proposal from UX team meeting that we agreed was not a hard blocker and could use further discussion).
- An issue for:
"Limit to accessed API's" or something as a starting point when you go into production.
...potentially dependent on #1537198: Add a Production/Development toggle.
- Potential issues for @gabesullice's ideas in #3.
- Ideas for promoting further security research on contrib code with JSON:API. Many contrib vulnerabilities continue to be found with REST, and if they also exist for JSON:API they become more severe because of JSON:API's more permissive configuration.
- Ideas for educating developers about writing secure code for an API-first Drupal.
- Other issues to harden the "GET everything by default" configuration.
- Issues for mitigating risks relating to config entities.
Comments
Comment #2
xjmI went ahead and parented all the existing issues (although a couple of them look to be sort-of-duplicates of each other). I think there's also several to file yet before we mark #3035979: [DISCUSSION] Should a facility be added to JSON:API which limits automatic exposure of all entities fixed.
Comment #3
gabesulliceThis was not proposed in the other issue because that issue was JSON:API specific and time-constrained.
I'm very interested in exploring ways that we can force entity type and field implementations to consider their security in the abstract, not tied to any particular representation (via Form API, JSON:API, config export, etc.).
I have lots of ideas for how we might do that:
AccessResult:: forbidden('Access logic has not yet been implemented for this (entity|field), follow the guidelines at <link> to implement a secure control.')fromEntityAccessControlHandler. Currently,checkFieldAccessreturns allowed by default (I'm not sure what it does at the entity level). This would be limited to Drupal 9 of course.AccessResultcalledUnconfirmedAccessResultand use that in the base implementation. We could then throw some deprecation notices when these are encountered as an end result. This object could have a->confirm()method on it to make it easy to affirm the default behavior in the base class, if it doesn't need to be overriden.view,create,edit,update, ...) are not clearly defined anywhere. We have loose conventions for what they mean, but no standardization. I know that changing them from strings would be highly disruptive, but what about creating a constant likeEntityAccessControleHandlerInterface::VIEW = 'view'with a clear definition of what it means, then making it a code smell to use raw strings?Finally, I'd like to suggest that we view API-First Drupal as an opportunity to make all of Drupal more secure...
There are far fewer moving parts to consider in the API-First world. There are no form alters, no Form API (no magic array keys!), no Twig templates, no ways to execute a DB query or render a View in a template, no places an entity might accidentally be exposed on a forgotten page via a custom block, ... the list goes on.
JSON:API exposes a Drupal site's data in such an unadulterated fashion, it provides a great mental exercise to gut-check your access results and it encourages better organized code. An implementor doesn't have to wonder if it's better to add an
#accessrender array key value or implement a field access hook. There's just one way to do it!In short, while it's a bit scary and opens up another surface to attack, the API-First architecture can bring a lot of clarity to a significantly muddied space.
</pontification> 😉
Comment #4
xjmLots of great stuff in #3, thanks @gabesullice!
Adding a few stragglers from #3035979: [DISCUSSION] Should a facility be added to JSON:API which limits automatic exposure of all entities that don't have issues yet to the remaining tasks.
Then, two more things that I think we should add are:
Another thing we should look at specifically is how to further harden the default "read everything" behavior. Most security team members were comfortable with our MVP in #3039568: Add a read-only mode to JSON:API, but a few raised concerns that this default behavior was still too permissive in terms of the risk of information disclosure.
One particular topic that came up is config entities, which many developers might not think of as being potentially available outside their modules' admin UIs. There's also the added risk that info disclosures with config entities might contain additional data that could be used to attack the site.
So adding those to the IS bullets as well!
Comment #5
effulgentsia commentedThat's #3040372: Allow for API-First content without requiring all configuration to also be API-first.
That's #3040364: Allow modules to declare the entity types they want to consume with HTTP APIs.
Comment #6
xjm@effulgentsia, I think #3040372: Allow for API-First content without requiring all configuration to also be API-first is great, but I think there might be additional things beyond #3040364: Allow modules to declare the entity types they want to consume with HTTP APIs that we should pursue to harden the read-everything behavior.
Comment #7
xjm@Wim Leers added #3043245: Allow marking a config schema types to be marked as "internal" (to avoid exposing them via HTTP APIs) for an issue we encountered for (un-)exposing Layout Builder defaults data, which adds data to the third-party settings of bundle config entities.
Comment #8
gabesulliceWhoa! Finally ready to get this going again.
I'd like for all of us to come to a consensus on what does and does not make sense to implement in order to further harden JSON:API from a security standpoint. In undertaking that process, I think we can also prioritize those things that we do want to tackle.
To help us accomplish that, I went through all of the child issues and aggregated them into a matrix of sorts. This matrix was useful to me because I found it pretty difficult to keep the many child issues of this discussion in my head at once and even harder to consider them relative to one another. Hopefully you all find it helpful as well.
For each issue, I created number of axes by which to evaluate the proposal:
Each proposal was ranked -1, 0 or 1, depending on whether the proposal had a negative outcome, no effect or a positive outcome.
Finally, I summed these values up and also ranked each proposal with a very rough level-of-effort (LOE). LOE is ranked 0 if it the work has already been done, 1 if it is something fairly straightforward, 2 if it is something that will take a moderate amount of work and 3 for the proposal that seemed most complicated to implement.
I think the two biggest shortcomings with this matrix approach is that it is fairly subjective and all of the axes are equally weighted. I've made the document open for public commenting. If you feel I've misrepresented whether something is negative, neutral or positive just comment on the cell. Same if you have a question. If I've missed an axis completely, just add a comment on this issue with the axis label and your ranking for each issue and I'll add it to the matrix.
Matrix available here.
Early conclusions:
There's a really nice correlation between things which are easier to implement and that have a positive impact. The two issues with the highest LOE are also among the issues that have the least positive outcome:
I propose we close these two issues:
On a more positive note, there is some low-hanging fruit:
I think we should make this one our first priority:
The issue with the highest positive outcome is thankfully among the middle LOEs.
I think these issues should be our second highest priority:
What about the rest?
@internalAPI to accomplish the same thing. This will make Drupal more stable regardless of its security outcomes so it doesn't need a priority under this meta issue IMO.internalthat muddies the water around this issue. I think we should leave it on this list of issues, but at the lowest of priorities.Comment #9
wim leers#8 🥳 👏👏👏👏👏👏
I am missing one axis: .
I transplanted your feedback/analysis of every issue onto the corresponding core issue, to ensure that people reading those respective issues and not this particular issue are also awrae.
RE: closed.
RE: first priority. Agreed.
RE: second priority:
Comment #10
gabesulliceI added your axis. I don't think it really changes my conclusions on its own (I have yet to read your comments on specific issues though). Notably, it pushed #3039687: Add an API read-only mode to Drupal core and #3037534: Add a mechanism to mark TypedData (entities and fields) internal via settings.php out of the "neutral" bucket into a slight positive. That makes sense to me.
Thanks for transplanting my comments! I'm going to go read all of your replies (and possibly reply to you). Then I'll post another comment back here summarizing any noteworthy updates to our thinking.
Comment #11
gabesulliceOkay, status update as promised:
Comment #12
wim leersComment #13
wim leersPatch created for #3068275: Add status report message about JSON:API's read-only mode.
Comment #14
wim leers#11 was a great status update. But that's 10 weeks ago. What's the status today?
That leaves only #3039715: Discuss whether we want an API routes audit status report and #3043245: Allow marking a config schema types to be marked as "internal" (to avoid exposing them via HTTP APIs). Everything else is either closed, RTBC or already has a plan.
Comment #15
xjmThanks for the update @Wim Leers!
To clarify, nothing should be blocked on or assigned to me -- these are all framework decisions, not release management decisions and way outside my personal expertise. I was posting a whole lot of issue comments summarizing discussions that I attended, without them being my specific requests personally. :)
Comment #16
xjmProbably should have done this awhile ago as well, but tagging for framework management feedback on the plan(s) as summarized in #11 and #14.
Comment #17
xjmSo the status on #3039687: Add an API read-only mode to Drupal core keeps confusing me; it's marked RTBC, but the request there is to wontfix it.
Comment #18
wim leers#17 because it needs a committer to sign off on it. At it would never get looked at. Perhaps I'm wrong there?
Comment #20
gabesullice@xjm just opened #3087525: [meta] Determine which core entity types should not expose their data to JSON:API (for DX or security reasons). I like its direction and I think it belongs as a child of this issue (so I made it one). It's an obvious companion to #14.1.