Drupal 10: Adding Extra User Account Protection

One of Drupal's strengths is its ability to create communities of users who contribute towards the content of the site. Whether you have an open forum, where users can create their own accounts, or a closed magazine site with just a few editors, you need to take the security of your users seriously.

Out of the box, Drupal has a number of account protection features that assist in making sure that users are authenticated correctly.

For example, the user login page is protected by a brute force system and will lock accounts after a number of incorrect password attempts in a short amount of time.

There are a few other things you can do to protect your site users that can be applied to any Drupal site. In this article we'll look through a number of different modules and techniques you can use to protect the user accounts on your site. We'll look at some of the pros and cons of each approach.

Flood Control

Drupal's login forms have built in brute force projection that will block any user account that fails to enter the correct password more than 5 times per IP address within an hour. This prevents automated bots from just guessing the password of a user account thousands of times until it hits the right combination.

The Flood Control module allows these settings to be tweaked to make them more (or less) restrictive.

A screenshot of the Flood Control module main form in the Drupal backend. Showing the different form options that can be set.

For example, you might want to set the failed login attempts to be 10 per IP address over a 10 minute time period, which might be useful if you have an office with a single IP address and your users keep locking themselves out of the site.

Flood Control also comes with a "unblock" form where you can clear any blocks on users that Drupal has added. This is actually a really useful feature as the only other way of clearing these blocks is by tinkering in the database.

A module called Login Security also exists that covers much of the same functionality of allowing administrators to configure the flood settings. That module differs by also issuing notifications (including Nagios alerts) if brute force login attempts are detected on the login form.

✅ Augments the existing security features of Drupal by allowing site admins to tweak the flood settings in Drupal

✅ Allows users to self govern their sites by clearing the flood status for other users.

✅ If you tweak the settings of the flood control module and find that your users are being locked out too often then you can easily tweak the settings again.

❌ You can possibly set the flood restrictions too low and make the feature essentially useless.

Username Enumeration Prevention

The flood feature in Drupal only works if the attacker either knows or can guess the username of the user, which is where the Username Enumeration Prevention module comes into play. This module has a number of different functions and will prevent anyone trying to guess how many users you have and what their usernames are by simply returning a 404 for user account pages.

Of course, you might allow anonymous users to view account pages, and if this is the case then the module will just warn you about this in the Drupal status page. Generally, though, it's best not to allow blanket access to your user profile pages unless you have a good reason to do so.

More recent versions of Drupal have changed the messaging that surrounds the login pages so its not longer possible to guess a user's username from the login form. If a user gets their password wrong Drupal will show the message "Unrecognized username or password", rather than telling them that just their username or password is incorrect.

Without information about who your users are an attacker has a much harder time in cracking into a Drupal user account.

The Rabbit Hole module is another module that can perform the same action, by masking the user profiles on your site. The Rabbit Hole might be better as it also provides the same protection to other types of content, although that depends on your use case.

✅ Simple, plug and play module that provides a simple feature.

❌ Doesn't actually have any functionality if your anonymous users can view user pages; although that functionality is not encouraged.

Tweak Session Settings

PHP has a number of settings that can be tweaked to change how sessions work and they are exposed in the services.yml file that forms part of your Drupal instillation.

The main settings you want to look at are:

  • gc_maxlifetime - The "gc" here stands for "garbage collection" and is the number of seconds after which the session data will be seen as garbage and is then removed by PHP. This setting is reset when the user visits the page, so PHP will only garbage collect sessions that have not been used in a while.

    This setting works closely with the gc_probability and gc_divisor settings, which are setup to run a garbage collection process in 1% of page requests. The default setting for gc_maxlifetime is 200,000 seconds, or 55 hours (about 2 days). After this time has passed there is a 1% chance that PHP will delete their session and they will have to log in again. It is possible to tweak these other settings to change how often old sessions are cleared, but 1% is usually a good value.

  • cookie_lifetime - This is the lifetime of the session cookie, in seconds, that the browser will keep hold of the session data for. The default for this value is 2,000,000 seconds, or 555 hours (about 23 days). This means that even if the user is using the site every day they will be logged out after 23 days and will need to re-authenticate.

    Setting this value to 0 will mean that the session will be cleared when the user closes their browser window. The user will still be able to use the site normally, but their session will be deleted if the browser window is closed.

As an example, we can set these settings to the following in the services.yml file.

parameters:
  session.storage.options:
    gc_maxlifetime: 25200
    cookie_lifetime: 0

This means that the max lifetime of a session is 25,200 seconds, or 7 hours and that the session will be deleted when the browser is closed. Setting these values means that users can use the site during their working day, and that the session will be deleted when they close their browser, of overnight, whichever is first.

23 days for a session to stay around can be quite a long time, especially on sites that have secure content. It is recommended to reduce this setting to be inline with your usage policies.

✅ This setting requires no modules and can enforce good session management by just tweaking a couple of values.

❌ Fiddling with these values can be annoying as setting them too low will mean your users will have to log back into the site too frequently.

Persistent Logins

In addition to tweaking the session settings the Persistent Login module can also be used to tweak the session times for users.

This module adds a little checkbox on the login page that, when ticked, will extend the lifetime of a session by a configurable amount. By default, sessions are extended by 30 days.

Persistent Login works by creating another session cookie that lives alongside the main session cookie. When the user ops in to the persistent login this additional session cookie is used to extend the life of the user's session by re-authenticating them if their main session has expired.

The module requires that the cookie_lifetime setting be set to 0. You also need to be careful of any reverse-proxy cache that your site uses as that can interfere with the session management the module provides. If this isn't configured then the reverse-proxy cache will ignore the persistent session cookie and serve cached pages instead.

✅ Works separately from the normal session cookies in PHP.

✅ Can be configured to extend the lifetime of the cookie every time the session is used.

❌ Needs configuration to the Drupal services.yml file and any reverse-proxy caches before use.

❌ Not recommended for use on all sites.

Automated Logout

Another module that deals with sessions is the Automated Logout module, which is more or less the opposite to the Persistent Login module.

Automated logout has quite a variety of options available, but will essentially log users out if their session exceeds a certain, pre-configured, time limit. By default, the module will set this to 1800 seconds (30 minutes). If this happens then the module can optionally inform the user that their session is about to expire and they can click a button to log back in.

When the logout module is about to expire the following dialog will be presented, giving the user the ability to extend their session or just log out of their account.

The dialog produced by the Automated Logout module, telling a user that their session is about to expire.

Of course, the dialog can be turned off as well, which will just log the user out and redirect them to the login page. The logging out is done via JavaScript so the site will essentially lock itself if left unattended.

This behavior is often found on banking websites, where your session will often expire after just a few minutes for security purposes. The idea being that if the user walks away and forgets to log out, then the site will log the user out on their behalf.

This limit can be configured globally, by user role, or even by user, meaning that you can give site administrators a stricter session limit than other types of users.

✅ Really useful if you want to strictly control the session times of your users.

✅ Can be turned off globally through a single checkbox.

✅ Controlling the timeouts by role means that you can give different levels of timeout depending on the security implications of the role.

❌ Can be annoying for end users when their site logs them out too much, make sure you set the auto logout limit to be sensible for your site.

Restrict By IP Address

If you want to really lock down your Drupal site then you can restrict access to the administration interface via the user's IP address. This can be done from a server configuration point of view, but there are a couple of modules that can also allow this.

The Restrict route by IP module is perhaps the best candidate for adding this security feature to your site as it can be used to restrict access to certain routes by IP address. Essentially, you can use the module to prevent access to the route "user.login", which allows your site to function normally, but doesn't allow the user login page to be accessed by users outside of a known IP address.

One flaw to this method is that you need to be careful about how you restrict the IP addresses. Some users do not have static IP addresses and so will not be able to use the site if this is the case. A good workaround to this is to use a VPN service with a known IP address and use this to lock down the login form.

The Restrict IP module is a good module; but is not ideal if you want to allow anonymous users to view your site. That's more of a blanket restriction of the site by IP address so that only users with known IP addresses can access the site; which might be good in certain circumstances.

✅ Simple and effective protection for the administration interface for a site with small numbers of users.

✅ Can be used to restrict access to other parts of the site, not just the login form.

❌ Causes an inconvenience for users who don't have a static IP address. This can be solved via VPN services but that causes an overhead for most users.

❌ Entirely possible to lock yourself out of your own site if you don't know what you are doing.

❌ Not suitable if you want lots of users from around the world to log into your site.

Prevent Or Restrict Access To "User 1"

Drupal has always had "special" user called "User 1", which is the first user to be created on the site (with the user ID of 1). This user essentially bypasses any permission or access check on the site, which allows this user full access to everything. User 1 is normally a development tool and is used by site builders to configure a Drupal site prior to launch.

If this seems like a terrible idea then you're not alone. In fact, the Drupal community has recently worked to remove this user from the system, which has . This means that User 1 will no longer be a thing when Drupal 11 is released (sometime in 2024).

Until that happens it's a good idea to restrict access to User 1. You can either block the user, or change the username and password of the user often (ideally every time you deploy).

✅ Blocking or at the very least restricting access to User 1 is generally a good idea.

Login Captcha

A captcha (or Completely Automated Public Turing test to tell Computers and Humans Apart) is a little element added to forms that can be used to prevent automated traffic. Whilst this is useful to prevent spam submissions, it can also be used on the login form to prevent automated submissions against the authentication area of a site.

The Captcha module can be used to add different types of Captcha to a Drupal site and is often coupled with the reCaptcha module to allow the Google reCaptcha service to be used. The Google reCaptcha service will perform some checks to see if you are a human and present you with a captcha of some sort if you fail those checks.

Adding the captcha module to challenge users on the login form is pretty simple. Whether it creates a good user experience or any level of security to the account is perhaps not that simple to answer. Drupal already comes with the flood protection system that is intended to prevent bots from guessing user passwords so it can be seen as somewhat redundant to add this to the login form.

The practice of adding captchas to sites to prevent spam submissions is commonplace, but there are lots of spam prevention mechanisms that should be tried before adding a captcha to the site.

✅ Easy to add captcha of different types to the login form to prevent automated access.

❌ Can create a terrible user experience, especially if you force your users to login often.

❌ Adding this to the user login form provides questionable security benefits.

❌ Many different types of captcha are not accessible.

Session Inspector

Users might log into a Drupal site from different locations, different devices, and even different browsers. Giving the user the ability to see their currently active sessions is a useful security measure and gives users ownership over their user profile.

The Session Inspector module shows the users about the currently active sessions they have open on the site. Using this information they can make decisions on what sessions they know about, and if they should still be active or not. The module allows users to delete sessions they don't want, which essentially logs them out of that browser or device.

Here is an example of the session inspector module in action.

A screenshot of the Drupal session inspector module, showing two active sessions for this user.

The location and browser information can look a little bit complex to users who users who aren't familiar with that information. To get around this the module allows different plugins to be installed that will format these parts of the session output accordingly. The BrowserDetector browser formatter module will format the browser information in a couple of different ways, for example, reducing the long user agent string down to just "Chrome". Instead of just displaying the IP address the Geocoder hostname formatter module can be used to convert the address into a physical location, which certainly helps users see where the session is apparently based.

If the permissions have been setup correctly, the session information for users can also be viewed by site administrators. This gives administrators the ability to control the sessions for the users on their site.

Full disclosure; I created and maintain the session inspector module. If you found it useful, or can think of any improvements then please let me know!

Other modules in this space that control the sessions a user has also exist. You can also install the Session Limit module to prevent users from having multiple sessions open or the Login History module to show their previous logins on their dashboard. I think giving the user information and some control over their sessions makes for a better user experience.

✅ Creates information that many users see on other platforms in Drupal.

✅ Users gain control over what sessions they have active on the site. Useful if they think their account has been compromised.

❌ Not all users will understand what this information represents.

❌ Might need more work to play nice with single sign on services.

❌ Removing a session sometimes isn't enough. If the account has been compromised then more action should be taken to secure it.

Logging User Activity

Drupal will create a log every time a user logs into the site, which is handy for establishing when users authenticate. The Login History module can be used to make another record of this information and show it to the user, but more logging is required if you want to track changes to user profiles.

The Events Log Track module is a useful way of providing extra logging to your Drupal site. It includes a sub-module called Events Log Track User that allows changes to user profiles changes to be logged, and even optionally recorded into the main Drupal log. Although the information is useful, the log doesn't inform as to what changed, just that a change occurred. You also need to do extra work in order to expose this information to your users, but it is useful for auditing and troubleshooting purposes.

In order to log that the password has changed some custom code must be created. Here is an example a hook_form_FORM_ID_alter() hook being used to inject a custom submit handler into the user profile form. If the submit handler detects that the password has changed then it will generate a log.

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_user_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['actions']['submit']['#submit'][] = 'mymodule_password_submit_handler';
}

/**
 * Submit handler for the "user_form" form.
 *
 * @param array $form
 *   The form.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The current form state.
 */
function mymodule_password_submit_handler(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if (!empty($form_state->getValue('pass') && $form_state->getValue('pass') != $form_state->getValue('current_pass'))) {
    // Log password change.
    $user = $form_state->getStorage()['user'];
    \Drupal::service('logger.factory')->get('mymodule')->info('Password changed for user {username}', ['username' => $user->name->value]);
  }
}

Again, this doesn't let your users know about the change, unless they have access to the latest logs report.

The Message module can be used to generate messages that the user can see in order to confirm that their password has changed. You can adapt the above code in the following way to generate the message entity to notify the user.

function mymodule_password_submit_handler(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if (!empty($form_state->getValue('pass') && $form_state->getValue('pass') != $form_state->getValue('current_pass'))) {
    // Log password change.
    $user = $form_state->getStorage()['user'];
    \Drupal::service('logger.factory')->get('mymodule')->info('Password changed for user {username}', ['username' => $user->name->value]);
    // Create message to inform the user of the change.
    $message = Message::create([
      'template' => 'user_password_changed',
      'uid' => $account->id(),
    ]);
    $message->set('langcode', 'en');
    $message->save();
  }
}

If you want to know more about how to generate messages for the user then you can read more about creating a notification system using the message and ECA modules.

Many sites will also email the user when their password is changed, which is a good idea as it informs the user of this important change to their account. This can either be handled by the message system, perhaps by using the Message Notify module. Even an email to the user who changed their own account is useful as it lets them know what sort of email to expect if their password does change.

✅ Creating a history of user interactions is useful for multiple purposes.

✅ Telling the user that their account has changed without their consent can help your users react to their accounts being compromised.

❌ Additional coding is needed to expose logs to the user or to send an email.

Password Policy

Having a strong password is a good idea on any site, but getting your users to enter a strong password can be a challenge. This is where the Password Policy module comes in handy. Using this module you can enforce a stricter standard of password for either all users or just users with a particular role.

Drupal already has a password quality tool that is built into the password creation field. This is only for informational purposes only as users can simply ignore the recommendation and enter any password they want.

Using the password policy module means that the password the user enters must adhere to the standards you dictate.

The password policy module is actually a suite of constraint modules that work together to strengthen the passwords on your site. Password policy comes with the following core constraints.

  • Character types - The number of different types that must be found in the password. The 4 support types are uppercase letter, lowercase letter, digits, and special characters. 
  • Character type count - The number of characters of a particular found that must be found in the password. For example, you can stipulate that the password must contain at least 1 upper case character.
  • Delay - The number of hours that must pass before the password can be reset.
  • History - Stores the previous hashed passwords for the user and checks that the hash of their new password doesn't match a previous hash.
  • Length - The minimum and maximum allowed length of the password.
  • Username - Prevents the user from entering their own username as part of their password.
  • Consecutive characters - Passwords must not contain more than a configured number of consecutive characters.
  • Disallowed list - Configure a list of passwords that cannot form all or part of the password.

Some of these effect the password being entered, whilst others effect the usage and history of the password. You can also couple this with the Password Policy Pwned module to add a policy to prevent your users from entering known compromised passwords.

What's more, the password policy module allows you to force a password reset for all users, or even just users of a particular role. Useful if you find that one of your accounts has been compromised or if the password of an account has been leaked.

The only thing you need to be careful of here is enforcing users to adhere to just a strict password policy that it becomes difficult to create a new password. They will start to dread having to create a new password, often causes them to enter into poor password storage policies like writing down their passwords on post-it notes. If your password page starts to act like The Password Game then you really need to step back and understand the problem you are trying to solve.

✅ Provides a way of enforcing secure passwords from your users.

✅ Ability to enforce a policy or force a reset for certain user roles is good.

❌ The password page may also need a bit of theming to get the policy to display correctly.

❌ The module can be a challenge to configure in a way that enforced complex passwords, but that won't annoy users.

GDPR

Turning things on their head a little, instead of securing the user account you can install the GDPR module to let the users know that you take their data privacy seriously. GDPR or General Data Protection Regulation is a EU and UK law that makes the personal data of the user the responsibility of the site and allows user to see (and remove) any personal data the site might have.

In it's most basic form, this module just provides a checklist that you can work through to ensure that your site conforms to basic GDPR principles.

The module also has a few extra sub-modules that can be installed to provide other functionality, like right to be forgotten requests and data sanitisation. A user can generate a data retrieval request and this can be completed by the site administrators to provide the user with all of the personal information that the site stores about them.

Due to the complexity in setup, it's best to start out with this module installed, rather than setup things after the fact.

✅ Allows site administrators to take their user's personal data seriously.

✅ Gives your users control over their own data.

❌ Can be painful to setup after the site has been established.

❌ Some actions require Drush commands.

Two-factor Authentication

A two-factor authentication system will add a secondary level of authentication to the system that means attackers will need both the password and a device in order to gain access to an account. This can be added using the TFA module which takes a little bit of configuration.

The TFA module requires the Key and Encrypt modules to be installed and configured correctly before TFA can be configured. Thankfully, there's plenty of documentation on how to do this in the TFA module readme file.

Once everything is configured running the module works in a self contained way. There's no need to setup external services as the authentication system will work with the services provided by the module. This includes presenting a user with a QR code to allow them to set things up easily and all the usual configuration options to turn on or off the system for certain user roles.

This might create a barrier to user’s who will need a second device in order to log into the site. If that device is missing or unavailable then the user won’t be able to access the site. The benefits of having TFA, however, are clear and will substantially protect the user accounts of all users who use it.

Note: As of writing the version 2.0.0-alpha2 is insecure as it has a known access bypass issue. Please use the current 1.6 version.

✅ A secure mechanism of authentication without relying on passwords alone.

❌ Fairly complex to implement. There's a lot of moving parts and they all need to be configured correctly to get this module working.

❌ Can be annoying to users if they need to reach for a TFA device if they have to authenticate often.

Login Disable

A slightly different approach to account protection is to disable the login form completely. This can be done using the Login Disable module, which has the ability to disable the login form unless the user provides a special parameter in the URL.

Here a screenshot of the login disable settings page.

The Drupal admin interface for the Login Disable button, showing the Prevent user login checkbox ticked and the Access key set to qwerty123. The other two settings are default.

When the module is activated you can set an access key that must be in the path when the user visits the login page to be able to log in. The above configuration settings changes the login path to be the following:

http://example.com/user/login?qwery123

This means that if a user without this path attempts to view the login page they will see a disabled form and a message letting them know that access to the site is restricted.

Login Disable also has a neat feature that can logout currently active users when it is activated.

This module is useful if you have a magazine or intranet site and you want to prevent people from logging in without first knowing what the special key is. You can make this easier for your users by using a bookmark that users can click on to enable the login form. Using this technique you can effectively block access to your site, but can be annoying for users if they don't have the secret key at hand.

I have used this module in the past in order to allow sites with a membership section to save resources during a busy event. By preventing users with certain roles from logging in during a big event we prevented the site from responding to lots of authenticated traffic and could use that resource to better handle the event.

✅ Disables the user login form for users who do not have the inside knowledge of how to login. Useful for magazine sites and intranets.

✅ Can be turned on temporarily if you need to prevent user access for any reason.

❌ Not useful for all Drupal sites.

Conclusion

There is a fine line to tread between 'secure' and 'annoying' with regards to your users. You'll obviously want to make things as secure as possible on your site, but it's quite possible to push that too far in the wrong direction and cause a barrier to your users. Adding too much security will just end up preventing your users from accessing your site.

Whilst the modules and techniques detailed here are useful, it is probably not a great idea to install everything and assume things are great; you will inevitably cause some problems. The best thing to do is start of with something like Flood Control and build up from there as that first step is not going to cause your users any problems. More intrusive modules like TFA should only be installed if you are absolutely certain that this is what you want. In my experience, a combination of things like short session times and TFA can cause users to get quickly annoyed and stop using the site.

In fact, configuring the security settings of your users in the wrong way can actually make things more insecure. For example, setting difficult password policies leads to poor password security and often leads to those passwords being leaked or even shared.

Having very short session times can also be an issue. I was once at a car dealership in the UK (mentioning no names) and I was chatting with the sales person who was entering details into the car dealership website. The session timeout value was set so low on the website that the sales person had to enter their password multiple times during the visit. They entered their password so many times that I ended up memorising it and suggested that they should probably change it once we left.

Security measures that treat users with disdain create perverse incentives that cause users to bypass the security in other ways.

The modules here should also not be your only focus in securing your user accounts. All users on your Drupal sites with have roles and permissions and it is essential to ensure that the permissions of your site are properly configured.

My approach to permissions has always been to give users the least amount of control, and only add more permissions if required. This stops any embarrassing security holes where your users have way too many permissions.

Whilst not always possible on all sites, I also tend to set up roles and permissions so that all users get just one or two roles. Doing this means that it's much easier to see what user has what permissions.

Finally, it goes without saying that you should keep your site up to date. There's no point in having a bunch of security modules installed, only to have your site compromised through an access bypass problem in the site.

Have I missed out any user account security measures here? Do you have any great stories on security measures causing perverse incentives? Let me know in the comments!

Comments

In reference to the section "Logging User Activity" above, there is a module "user_history", that will track changes to a user account for audit purposes, if that is required.

Disclosure: I am the maintainer of the module.

Permalink

Thanks for the info James, much appreciated!

Name
Philip Norton
Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
20 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.