Users' email addresses are one of the most important types of data when it comes to restricting access and keeping them private. A breach of confidentiality is often punishable by law with substantial fines.

The user entity had correspondingly tight access: 'access user profiles' permission gives access to the user, but the email field requires full 'administer users' permission. If an admin creates a view of users, there is no risk of exposing data even if the view has no access check.

Install simplenews module, and suddenly everything is wide open. The Subscriber entity has no access checks at all. If an admin creates a view of users with no access check, then the email addresses are public. This is not just theoretical - for a brief moment I made this mistake on a site one time.

Comments

AdamPS created an issue. See original summary.

adamps’s picture

Assigned: Unassigned » berdir

@Berdir I am willing to code this, but I would like to check with you first that you agree it's a good idea. When you have some spare time, please can you let me know? Thanks.

berdir’s picture

Assigned: berdir » Unassigned

Yeah, a lot of contrib didn't bother with field-based access as that's a new thing and wasn't really required to get things working. Core is very inconsistent too, e.g. media has a bunch of open issues around this.

Happy to improve that, that said, I'm not quite sure how that applies to subscribers, as they as a whole should be equally restricted? Field access applies when different fields have different access, e.g. user name being public and user e-mail private. So field access is generally on top of entity access, so field access is only checked when you have access to the entity.

There doesn't seem to be a reason to create a view of subscribers where some of the data should be public? So the bigger issue there is that views defaults to no access checks unless you have a custom wizard that sets a default permission?

adamps’s picture

Thanks @Berdir. Apologies, I didn't actually describe what access checks I planned to add. I agree that the subscriber entity should have entity-level access and does not need field-level access. I will add a new permission 'view simplenews subscriptions' that controls access to the subscriptions of other users. Users can always view their own subscription.

So the bigger issue there is that views defaults to no access checks unless you have a custom wizard that sets a default permission?

I don't see that as an issue. Once we've added the entity access, it doesn't really matter if the view has no access checks - users without subscriber entity access will just see an empty page. This is the same for all entity types in core and contrib.

adamps’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new3.04 KB

Here is a first attempt at a patch. No tests yet.

Status: Needs review » Needs work

The last submitted patch, 5: simplenews.access.3032167-5.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review
StatusFileSize
new2.55 KB
new360 bytes

Guess we better not change the default access on the view

Status: Needs review » Needs work

The last submitted patch, 7: simplenews.access.3032167-7.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

berdir’s picture

Entity access defaults to no access, unlike fields which default to being accessible. I don't think views checks entity access like you described it, or what you did would not have been possible if the only thing we have atm is an admin permission :)

So this patch does not restrict access, it opens it up based on a new permission.

adamps’s picture

StatusFileSize
new2.77 KB

@Berdir Yes you are right. We can protect access to the mail field, which then matches the user entity.

adamps’s picture

Status: Needs work » Needs review
adamps’s picture

The last submitted patch, 12: simplenews.access.ONLYTESTS.3032167-12.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 12: simplenews.access.3032167-12.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review
StatusFileSize
new6.03 KB
new947 bytes
adamps’s picture

Assigned: Unassigned » berdir
Issue tags: +Plan to commit

SubscriberAccessControlHandler has two parts

1. checkFieldAccess restricts access to the mail field, similar to UserAccessControlHandler. This part solves the problem described in the IS.

2. checkAccess grants access. If we are going to have a permission 'view simplenews subscriptions', then it should for sure allow that access. As @Berdir pointed out, this doesn't make any difference to views, which doesn't check entity access at all. It still seems like a good idea to make other ways of viewing subscribers work (Ajax/JSON etc?).

@Berdir - I would be very grateful for your review of this one please to ensure I don't accidentally create a security bug.

adamps’s picture

Here is a new patch that corrects some errors in the previous patch, but also fixes some problems in the current code.

Where I say "SAFEGUARD" below this indicates extra checking that is a good general principle, but not really a current bug except in case of editing by some other means such as JSON.

  1. [MAJOR BUG] Deny access to edit email of a subscriber linked to a user. This is important because it otherwise it makes a real mess: the user and subscriber are linked but have a different email address.
  2. [SAFEGUARD] Deny access to edit langcode of a subscriber linked to a user (SubscriberForm already checks).
  3. [SAFEGUARD] Deny access to edit uid.
  4. [NEW] Admin only edit of status and created - necessary now that we have granted users access to edit their own entity.
  5. [MINOR BUG] Deny access to /user/0/simplenews
  6. [MINOR BUG] Correct the formatter name 'email' should be 'email_default' (it works because email_default is the default anyway). Also remove pointless calls to setDisplayConfigurable() because there is no GUI to configure it anyway.

Status: Needs review » Needs work

The last submitted patch, 17: simplenews.access.3032167-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review

OK, last fail was just a glitch. This is ready for review. I won't commit it until @Berdir has had a chance to look even if that means waiting a while because this is subtle and important.

berdir’s picture

  1. +++ b/src/Entity/Subscriber.php
    @@ -356,18 +357,16 @@ class Subscriber extends ContentEntityBase implements SubscriberInterface {
             'weight' => 5,
    -      ))
    -      ->setDisplayConfigurable('form', TRUE);
    +      ));
    

    yeah, this doesn't make sense to be configurable, was there since the first 8.x commit basically and probably copy & pasted from somewhere.

  2. +++ b/src/Form/SubscriberForm.php
    @@ -28,13 +28,14 @@ class SubscriberForm extends ContentEntityForm {
           '#title' => t('Active'),
           '#default_value' => $subscriber->getStatus(),
    +      '#disabled' => !$subscriber->status->access('edit'),
         );
    

    I prefer using get('status') especially in cases like this when you actually use methods on the FieldItemList object and benefit from autocomplete and code inspection.

  3. +++ b/src/Form/SubscriptionsFormBase.php
    @@ -132,7 +132,6 @@ abstract class SubscriptionsFormBase extends ContentEntityForm {
     
         // Modify UI texts.
         if ($mail = $this->entity->getMail()) {
    -      $form['mail']['#access'] = FALSE;
           $form['subscriptions']['widget']['#title'] = t('Subscriptions for %mail', array('%mail' => $mail));
           $form['subscriptions']['widget']['#description'] = t('Check the newsletters you want to subscribe to. Uncheck the ones you want to unsubscribe from.');
         }
    diff --git a/src/SubscriberAccessControlHandler.php b/src/SubscriberAccessControlHandler.php
    

    the title was changed here because the e-mail wasn't visible anymore I think, so maybe that part isn't necessary anymore? not 100% related, so possibly better left for some other UI improvement issue.

  4. +++ b/src/SubscriberAccessControlHandler.php
    @@ -0,0 +1,89 @@
    +    // keep a record of the subscription history.
    +    $is_own_account = $account->isAuthenticated() && ($entity->getUserId() == $account->id());
    +    if ($is_own_account && ($operation != 'delete') && $account->hasPermission('subscribe to newsletters')) {
    +      return AccessResult::allowed()->addCacheableDependency($entity)->cachePerPermissions();
    +    }
    +
    

    this access check does also vary on the user not just the entity. if either the user or the entity is different, then the access result may be different.

    Also, the cache contexts need to be outside of at least some of these conditions, imagine this:

    * user A who is not the user connected to subscription 1 tries to edit it, he is not owner, => access denied.
    * user B who is the connected user tries to access it but the previous check was cached without considering the current user, so access is denied.

    What you can do to optimize it is to check the permission and operation first, then add the cache contexts, then check if it is his subscription.

    Allowing this certainly makes sense for e.g. decoupled scenarios but the question is how the user finds "his" subscription in the first place, without being allowed to list them :) Because if you compare that with the default UI, that is always exposed through local tasks/a page/blocks where we look up the entity for the user. So to be able to actually make this work for decoupled sites, we'd need some kind of API that allows users to either get the ID of their subscription or a special route that works similar to the page and looks up the right subscription.

    Just saying that implementing conceptually correct but not very practical ye.

Other than that stuff, this looks good, I'd just like to point out one last time that this still does not lock down access as the issue summary claims except denying edit access for the mail/langcode field if connected to a user. Everything actually adds cases where we allow more access than before. Currently we rely on "admin_permission = "administer simplenews subscriptions",", which means *anything* is only possible with that permission, now we also respect own access as well as viewing subscriptions.

berdir’s picture

Assigned: berdir » Unassigned
adamps’s picture

2. Done

3. Prior to this fix, SubscriptionsFormBase was the base class for 3 different forms, and in each case email was not visible. Just to be clear, after this fix, email is still not visible on those 3 forms. In the new code, it is checkFieldAccess() that hides the email.

4. Done, thank you - I knew you would be a great reviewer for subtle bugs in this code! It turned out to be the operation and user ID that I needed to check first.

===

Finally, thanks yes I understand that checkAccess grants access, see #16. So yes it isn't an essential part of the fix and I agree that it's hard to find a way to use the new access right now.

Even so I believe it is important to be in this patch.

  1. Now we have an AccessControlHandler it's confusing if it is not accurate.
  2. It could easily be useful to contrib/custom hook code, or in some future version of the module.
berdir’s picture

Will check again later, to be clear, my last comment was simply about the issue summary being out of date and incorrect and we should update it because it is confusing. It does not and can not fix the mentioned views problem, the only thing that would help with that is if we'd provide a views wizard thing that sets a default access check. I agree with the changes here, but don't think it fixes any security issues. That's all I'm trying to say. That this is more a task/refactoring than a (major) bugfix.

adamps’s picture

Title: Missing access check for subscriber entity » Missing access check for subscriber mail field

I changed the issue title slightly to clarify it is the field access that is missing.

It does not and can not fix the mentioned views problem

Well it works for me and is tested in SimplenewsAdministrationTest::testAccess().

Views makes a call to checkFieldAccess with items = NULL and if that fails it hides the email column.

adamps’s picture

StatusFileSize
new10.06 KB

Reroll

@Berdir I plan to commit this in one week. After one more week I will create a new release. Please comment if you don't want me to do that.

Status: Needs review » Needs work

The last submitted patch, 26: simplenews.access.3032167-26.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review
StatusFileSize
new10.06 KB
new516 bytes

Fix deprecation

Status: Needs review » Needs work

The last submitted patch, 28: simplenews.access.3032167-28.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review

Test failures are now same as in dev branch.

berdir’s picture

Status: Needs review » Reviewed & tested by the community

Feel free to go ahead.

> Views makes a call to checkFieldAccess with items = NULL and if that fails it hides the email column.

True. My point is that having a subscriber view exposed to anyone makes no sense in the first place. Yes, it does hide the e-mail field, but what I was trying to get at is the the view as a whole should default to a sensible permission. Certain columns might still remain visible and configurable fields would too. But the only way to have that is a custom views wizard thingy that sets a default permission, an access control handler can't fix that.

  • 2ec6a02 committed on 8.x-1.x
    Issue #3032167 by AdamPS, Berdir: Missing access check for subscriber...
adamps’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Plan to commit

@Berdir Thanks. I agree. The subscriber view should not be exposed and this issue does not fix that. However it makes the consequences of exposing it much less bad.

adamps’s picture

Status: Fixed » Needs review
StatusFileSize
new518 bytes

Sorry I was wrong about removing setDisplayConfigurable for the mail field. It is configurable at /admin/config/people/simplenews/settings/subscriber/form-display, page which I only just discovered.

  • 0436b93 committed on 8.x-1.x
    Issue #3032167 by AdamPS: Missing access check for subscriber mail field
    
adamps’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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