WordPress.org

Make WordPress Core

Opened 2 years ago

Last modified 6 weeks ago

#43936 reviewing feature request

Settings: Warn when open registration and new user default is privileged

Reported by: kraftbj Owned by: SergeyBiryukov
Milestone: Future Release Priority: normal
Severity: normal Version:
Component: Users Keywords: has-patch needs-refresh
Focuses: administration Cc:

Description

Much like our Strong Passwords work, we can help inform site administrators when their actions may be sub-optimal.

WordPress allows a site owner to open registration AND set the default new user level to "Administrator". While this combination may make sense for some sites (on an intranet?), this is typically a really really bad idea.

We should provide some type of confirmation to ensure site administrators are intending to open their site up.

If registration is open and default level is Subscriber (read cap only), the current behavior is fine. If registration is open and other capabilities are included in the default role, we should have some type of checkbox or "are you sure" notice. "By allowing open registration and a default role of {role}, anyone who can visit the site would have the ability to {have full control of your site|publish content|etc}."

We saw this in the wild on a site in support today :).

Attachments (3)

43936.diff (834 bytes) - added by subrataemfluence 2 years ago.
43936.2.diff (1.8 KB) - added by kraftbj 2 years ago.
43936.3.diff (2.1 KB) - added by kraftbj 2 years ago.

Download all attachments as: .zip

Change History (30)

#1 @subrataemfluence
2 years ago

  • Component changed from Administration to Users
  • Focuses administration added

Good catch!! Rather having a checkbox to "confirm", I would rather suggest to remove the "Administrator" option from the dropdown from this page. Since admin always has the ability to change a user's role he can easily do so by going to that user's profile any time.

I agree that opening Administrator privilege for a new user registration should not be an option anyway!

#2 @subrataemfluence
2 years ago

  • Keywords has-patch added

I have added an additional optional parameter which will allow to pass an array of roles which need to be excluded from the dropdown.

Example:

<?php
wp_dropdown_roles( get_option('default_role'), array( 'administrator', 'editor' ) );

The above roles won't render Administrator and Author in the dropdown.

However, if called like

<?php
wp_dropdown_roles( get_option('default_role') );

All roles will be available in the dropdown like now.

Version 0, edited 2 years ago by subrataemfluence (next)

This ticket was mentioned in Slack in #gdpr-compliance by desrosj. View the logs.


2 years ago

#4 @kraftbj
2 years ago

Thanks for the patch @subrataemfluence! A couple of logistical notes on the patch - It is helpful to run the svn diff from the root so you have the full path (e.g. src/wp-admin/includes/template.php instead of just template.php). That'll make it easier to apply to patch.

Also, be sure your code follow WordPress Coding Standards ( https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/ ). There were a few spacing issues that I noticed.

Submitting a follow-up patch that includes updated inline docs, coding standards compliance, and implements the modified wp_dropdown_roles to resolve the ticket issue.

@kraftbj
2 years ago

#5 follow-up: @kraftbj
2 years ago

A couple of things:

  1. We should add a filter so plugins can add additional roles (e.g. Keymaster) to exclude or to allow more roles (e.g. a private intranet where one _does_ want to allow new users as Admins).
  1. Does it make sense to exclude roles or exclude certain capabilities?

@kraftbj
2 years ago

#6 in reply to: ↑ 5 @subrataemfluence
2 years ago

Thank you for further fine tuning my proposed patch and thumb up for the introduction of the additional filter!

By the way, I forgot to upload the patch for template.php! :P

Replying to kraftbj:

A couple of things:

  1. We should add a filter so plugins can add additional roles (e.g. Keymaster) to exclude or to allow more roles (e.g. a private intranet where one _does_ want to allow new users as Admins).
  1. Does it make sense to exclude roles or exclude certain capabilities?

#7 @subrataemfluence
2 years ago

I would rather suggest this ticket to be labeled as "Feature request" since the current functionality does not have any bug in it.

#8 @roytanck
21 months ago

We've seen a couple of plugin vulnerabilities recently that allowed attackers to set these options, even while unauthenticated.

The obvious attack vector was to enable registration and set the default role to admin. This was not done through the admin settings page, but through manipulated URLs.

Besides not offering the option in the dropdown, I think core should also not add the user if this combination of settings exists.

Personally, I can think of no use case that would require this combination of settings. It's essentially "please take my site".

#9 @desrosj
14 months ago

#46744 was marked as a duplicate.

#10 @dd32
14 months ago

#46744 was closed as a duplicate of this, which I agree with.

The main difference is that this is a warning/only allows selecting safe values in the UI, where #46744 focuses on the malicious setting of options to bad values through a vulnerability that allows setting of options (of which, are common in recent years in plugins).

Preventing a user selecting a dangerous combination is needed, but it also needs to validate that the values in the database are safe to rely upon IMHO
As an example, filter on the default value:

function filter_default_role( $default_role ) {
  // $users_can_register = ....
  if ( $users_can_register && get_role( $default_role )->has_cap( 'manage_options' /* or other cap deemed useful, `publish_posts` could also be used */ ) ) {
    $default_role = 'subscriber'; // Fallback roll for when an unsafe roll has ended up in there
  }
  return $default_role;
}

#11 @desrosj
14 months ago

#46661 was marked as a duplicate.

#12 @ottok
9 months ago

I think that both this and #46744 would best be solved by completely preventing the default_role from having the values for 'administrator' and 'editor'. If the database has either of these values, it should just be ignored.

This would categorically fix a whole category of SQL injections that use this trick get admin access to the site. See for example https://www.slideshare.net/ottokekalainen/how-to-investigate-and-recover-from-a-security-breach-in-wordpress#29

I am willing to write the patch + unit tests to make sure that if the database has either of these values, it would be ignored, and that in the UI admins can't set the setting to the forbidden values.

I don't see any valid use cases to allow all users to have admin or editor role by default. It is very easy to make a new user with specifically this user role, there is no need to have extra automation to facilitate this and at the same time open a gaping security hole. The trade-off to me is clear: block these dangerous values and let users set user roles in other ways.

Do you agree? Do you want me to write the patch? Would somebody sponsor putting it in then?

#13 @ottok
9 months ago

Also this should be changed: https://wordpress.org/support/article/settings-general-screen/#new-user-default-role

Valid choices are Administrator, Editor, Author, Contributor, or Subscriber.

#14 @SergeyBiryukov
8 months ago

  • Milestone changed from Awaiting Review to 5.4
  • Owner set to SergeyBiryukov
  • Status changed from new to reviewing

#15 @jrf
8 months ago

  • Keywords needs-refresh added

I've read through this and all related tickets at @ottok's request.

Great input and patches. Thanks everyone who has contributed so far.

Based on everything I've read, I see two distinct points of entry to make changes:

  1. The default user role selection drop down on the Options -> General page (display).
  2. The update_option() call to update the value for default_role (saving).

For the default user role selection drop down, I would like to suggest the following taking all input given into account:

  • Get the default_role from the database.
  • If registration is open (users_can_register is enabled), allow the "excluded roles" to be filtered. By default, set this to administrator and editor.
  • If registration is open, don't allow administrator as the default role *ever*. The editor role should be allowed, but only when explicitly removed from "excluded roles" via the filter, not as a role available by default.
  • If registration is open and the output of the filter would have removed administrator from the "excluded roles", add back administrator and throw a _doing_it_wrong(). This will allow sysadmins to pick up on this being attempted in their error logs.
  • Use the output of the "excluded roles" filter in the wp_dropdown_roles() function as proposed in the current patches to limit the roles displayed in the dropdown.
  • If the default_role is set to one of the "excluded roles", use subscriber instead. This will also prevent an existing default role of administrator coming from the database from being used.

I agree that using the existing 'editable_roles' filter which filters the roles which will be displayed via the wp_dropdown_roles() function is not strong enough protection as a secondary filter running after "our" filter could undo the removal of administrator. The current patches already take this into account.

For the saving of the option part, we could add a filter hooked into pre_update_option_default_role to check if the value is administrator and if so and only if registration is open, revert it back to subscriber.

I would also like to see some unit tests added for each of these situations to safeguard this change from accidentally being reverted in the future, think tests along the lines of:

  • Registration open and default role in database is administrator => actual default role is set to subscriber.
  • Registration open and default role in database is something else => actual default role is the same.
  • Registration closed and default role in database is administrator => actual default role is set to administator.
  • Registration closed and default role in database is something else => actual default role is the same.
  • Registration open and default role in database is subscriber, filter on the option changes it to administrator => actual default role is set to subscriber.
  • Registration open and default role in database is subscriber, filter on the option changes it to editor => actual default role is set to editor.
  • Registration closed and default role in database is subscriber, filter on the option changes it to administrator => actual default role is set to administrator.
  • Registration closed and default role in database is subscriber, filter on the option changes it to editor => actual default role is set to editor.

... etc ...

This all could still be bypassed by unhooking the save filter and/or using actions before and after the dropdown display to change registration from open to closed and back again, but for that a malicious plugin would already need to be installed. As far as I c currently see, the above proposed process flow couldn't be bypassed just by manipulating URLs.

Another thing to consider, but this will need further discussion: what about adding a check in the upgrade routine for WP 5.4 to verify the default_role and if 1) registration is open and 2) the default role is set to administrator change it subscriber ?
This will break expectations for site owners which have set the default role to administrator on purpose (intranet), but would - in one go - make all sites where a hack has taken place which has changed this value without the site owner being aware, secure again.

Either way, I hope this feedback helps.

Do you agree? Do you want me to write the patch? Would somebody sponsor putting it in then?

@ottok I'd love to see a patch implementing this and will definitely support such a patch to go in.

Also this should be changed: https://wordpress.org/support/article/settings-general-screen/#new-user-default-role

@ottok Good catch and yes, I agree.

#16 follow-up: @ottok
8 months ago

  1. The update_option() call to update the value for default_role (saving).

This would not protect against the SQL injections I referred to. I was thinking of making a patch that affects fetching the option from the database, and if the database value is 'administrator', the code would ignore that value and return 'subscriber' instead.

#17 in reply to: ↑ 16 @jrf
8 months ago

Replying to ottok:

  1. The update_option() call to update the value for default_role (saving).

This would not protect against the SQL injections I referred to. I was thinking of making a patch that affects fetching the option from the database, and if the database value is 'administrator', the code would ignore that value and return 'subscriber' instead.

You're completely correct, though it would prevent saving of the invalid value from within the WP framework.

An additional filter on the option_default_role, as you suggest, could help in that regards 👍. Just keep in mind that any filter can be unhooked.

#18 @eatingrules
7 months ago

I'd like to add another vote here to not allow new user default roles to be Editor or Administrator if "Anyone can register" is enabled.

We had a client this morning discover that all new accounts her site were being created as Administrators... She became aware of it only once a customer pointed out to her that she had been granted Admin access after she purchased. We have no idea when/how/why the default setting changed to Administrator (thankfully, at this point haven't found any evidence of other malicious behavior).

Thanks!

This ticket was mentioned in Slack in #core by david.baumwald. View the logs.


5 months ago

#20 @SergeyBiryukov
5 months ago

  • Keywords early added
  • Milestone changed from 5.4 to 5.5

Looks like this still needs some work per the latest comments, moving to early 5.5 for now.

This ticket was mentioned in Slack in #core by david.baumwald. View the logs.


4 months ago

#22 @davidbaumwald
4 months ago

@SergeyBiryukov Is this still on your list to handle early in 5.5?

This ticket was mentioned in Slack in #core by david.baumwald. View the logs.


3 months ago

This ticket was mentioned in Slack in #core by david.baumwald. View the logs.


2 months ago

#25 @Hareesh Pillai
2 months ago

  • Type changed from defect (bug) to feature request

#26 @davidbaumwald
6 weeks ago

  • Keywords early removed

Removing the early tag per discussion with @SergeyBiryukov

#27 @SergeyBiryukov
6 weeks ago

  • Milestone changed from 5.5 to Future Release
Note: See TracTickets for help on using tickets.