EntityAccessControlHandler::createAccess() and EntityAccessControlHandler::access() return false positive cache hits because it ignores context

Created on 16 June 2017, over 7 years ago
Updated 28 February 2024, about 1 year ago

Problem/Motivation

Currently the EntityAccessControlHandler::createAccess() method does not take the optional $context array into account when statically caching the access result. This leads to unpredicted behaviors when you want to perform create access checks with different contexts.

For example, think of an entity that might only be created if some context parameter has a special value (e.g. the ID of a parent entity). When performing several access checks with different context values, the first call gets cached and its result is always used regardless of what the context array contains.

Current code is:

$cid = $entity_bundle ? 'create:' . $entity_bundle : 'create';
if (($access = $this->getCache($cid, 'create', $context['langcode'], $account)) !== NULL) {
  // Cache hit, no work necessary.
  return $return_as_object ? $access : $access->isAllowed();
}

As you can see, only $context['langcode'] is respected when statically caching the create access result.

The only solution right now, is to completely override the createAccess() method with custom logic for the cache ID in an access handler (if you have the chance to do so).

Proposed resolution

  • Move cache id generation into a helper method, so it is easier for inheriting classes to customise.
  • By default, skip static caching when there is context other than langcode.

Remaining tasks

  • Review patch

User interface changes

n/a

API changes

n/a

Data model changes

n/a

๐Ÿ› Bug report
Status

Needs work

Version

11.0 ๐Ÿ”ฅ

Component
Entityย  โ†’

Last updated about 22 hours ago

  • Maintained by
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom @catch
  • ๐Ÿ‡จ๐Ÿ‡ญSwitzerland @berdir
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany @hchonov
Created by

๐Ÿ‡ฉ๐Ÿ‡ชGermany hctom

Live updates comments and jobs are added and updated live.
  • Security

    It is used for security vulnerabilities which do not need a security advisory. For example, security issues in projects which do not have security advisory coverage, or forward-porting a change already disclosed in a security advisory. See Drupalโ€™s security advisory policy for details. Be careful publicly disclosing security vulnerabilities! Use the โ€œReport a security vulnerabilityโ€ link in the project pageโ€™s sidebar. See how to report a security issue for details.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • ๐Ÿ‡ท๐Ÿ‡ดRomania claudiu.cristea Arad ๐Ÿ‡ท๐Ÿ‡ด

    This is at least Major if not Critical

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    We had converged on consensus on the solution in #36. But #48 makes this point:

    including the $context that's in createAccess() isn't a complete fix for this bug: A module might implement hook_entity_create_access() and return an access value that's based on something else. It would correctly add a cache tag for that dependency into the returned AccessResult, only to have that ignored by the static caching.

    I think this is a valid concern.

    It seems therefore that in order to fix the bug here, we should use MemoryCache.

  • Status changed to Needs review about 1 year ago
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    I think I'm wrong above in #55.
    I don't see a way to get information back from the cacheable metadata added by hooks about what createAccess() $context they used in their logic. Therefore we cannot handle context intelligently even with MemoryCache.
    So we either have to serialize the whole context, which has performance concerns, or we just don't do static caching when there is context. Which is the approach in #36 we mostly all like.

    This is valid regardless of whether we use MemoryCache or not in the future. $context could contain anything, and so we shouldn't automatically be stuffing it into MemoryCache either.

    MemoryCache would solve the issue identified in #48, but it's not the right solution for this, so let's rule #48 out of scope here and move it to another issue.

  • Status changed to Needs work about 1 year ago
  • The Needs Review Queue Bot โ†’ tested this issue.

    While you are making the above changes, we recommend that you convert this patch to a merge request โ†’ . Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)

  • Status changed to Needs review 5 months ago
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    #46 is wrong to say "this affects access() as well as createAccess()." access() does not use $context. It's true that cacheability metadata of hooks and handler results in both access() and createAccess() is not considered. This is a bug, and the correct solution is a memory cache. But it's a different bug to this issue, and solving it does not necessarily solve the context bug discussed in this issue because context may contain items that are not cacheable dependencies. I created ๐Ÿ“Œ Use a memory cache in EntityAccessControlHandler Active to address this other bug.

    The MR is based on #33/36, which @hctom in #37 and @claudiu.cristea in #44 supported. We cease to use the cache if a context is provided. As discussed in #36/44 this may have a performance impact on some custom code, but that's better than the present situation of a bug with possible security implications. As @hctom said in #37:

    Everything is better than getting wrongly cached results that might even result in security issues!

    Improving performance by caching even if context is present is a task for the follow-up: โœจ EntityAccessControlHandler::createAccess() should use whole context array when caching Active .

  • Pipeline finished with Failed
    5 months ago
    #310407
  • Pipeline finished with Success
    5 months ago
    #310479
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Left some comments on the MR.

  • Pipeline finished with Failed
    3 months ago
    Total: 159s
    #366084
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK
  • Pipeline finished with Failed
    3 months ago
    Total: 636s
    #366121
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK
  • Pipeline finished with Success
    3 months ago
    Total: 508s
    #366610
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    This is a blocker (not sure if soft or hard) for contrib in ๐Ÿ“Œ Add 'create * host permissions' Postponed

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany geek-merlin Freiburg, Germany

    This needs a different approach.
    The access result can vary by all sorts of context ($context, dereved from it, and external ones). This is the role of cache contexts to be returned with $accessResult (maybe from a hook_access_alter). These cache contexts can not be known in advance.
    Sounds familiar? Yes, this is exactly what VariationCache is for.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    This is a bug. And one with security implications. So in this issue probably we should fix it in the quickest, simplest way we can.

    Creating a good modern-Drupal caching system for context is the subject of ๐Ÿ“Œ Use a memory cache in EntityAccessControlHandler Active . I'd be grateful if you could share the idea of using VariationCache there.

    What things are passed in $context anyway?

    We don't know, by design. The API is intentionally agnostic. That's a part of the challenge.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany geek-merlin Freiburg, Germany

    Thanks for the reply. You are right, mostly.

    But what about this: the $context (and things derived from it) CAN be used in hook_entity_create_access_alter, with no way to broadcast that fact back to the AccessControlHandler.

    ???

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom jonathanshaw Stroud, UK

    But what about this: the $context (and things derived from it) CAN be used in hook_entity_create_access_alter, with no way to broadcast that fact back to the AccessControlHandler. (And it may add the need for additional caching contexts / $cid additions.)

    Yes, hooks can use anything provided as by context and the handler doesn't know what they used. So everything in context needs to be considered when addressing caching, even if its not used by the handler. Things derived from context don't matter, they should be handled by caching context, and its up to hook implementations to add cacheable metadata themselves if they do anything more exotic.

    But none of this matters in this issue. We already have a context. And we have caching. And caching ignores context, leading to false postives which are a bad thing, and so we should not use our current caching if we have context because no caching is much better than bad caching. And we should do this now, because its broken now and easy to fix, whereas making cool ways to cache context for edge cases is hard.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany geek-merlin Freiburg, Germany

    So it's up to the AccesControlHandler to add all "possible" $context variations to $cid. And if other things are passed in $context and used in an access hook, that is not supported (which may go to the docs...).

    It's not my intent to bloat this, just to clarify.
    Thanks a lot for pushing this forward!

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany geek-merlin Freiburg, Germany

    Did a code review of the MR.

    The factored out ::buildCreateAccessCid lgtm, it does what it announces, and defaulting complex cases to uncacheable seems reasonable.
    Test coverage looks reasonable, though i did not think about each case. (Only found an รœbernit in the code.)
    Setting RTBC. You should revert though that if you think that more thorough test review is needed.

  • First commit to issue fork.
  • ๐Ÿ‡ณ๐Ÿ‡ฟNew Zealand quietone

    I read the IS and the comments. I didn't find any unanswered questions or other work to do. Reading the MR I found some coding standards to fix and I removed an unused variable from the test. These are minor, and I doubled checked, so I am leaving at RTBC, also updated credit a bit.

    • catch โ†’ committed 36dda075 on 11.x
      Issue #2886800 by jonathanshaw, claudiu.cristea, quietone, hctom, geek-...
    • catch โ†’ committed 3a8edbfd on 11.1.x
      Issue #2886800 by jonathanshaw, claudiu.cristea, quietone, hctom, geek-...
    • catch โ†’ committed 34e9c0c2 on 10.5.x
      Issue #2886800 by jonathanshaw, claudiu.cristea, quietone, hctom, geek-...
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch

    I think the approach of skipping caching if $context is non-standard is a good one. If someone runs into a problem with that, they might come up with a performant way to cache with arbitrary context.

    Committed/pushed to 11.x, 11.1.x, and 10.5.x, thanks!

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024