One of the common issues I've noticed when working with customers is the tendency to treat non-production environments, such as dev or stage, as less important with respect to security.

This is understandable since these environments are effectively disposable and could be rebuilt from production at any time. However an important consideration that should be taken into account is what data lives in these environments.

Most customers I talk to are mostly concerned with sites "getting hacked." This mainly refers to the site contents being changed publicly. Often restricting access to sensitive data is a lesser concern, but it should be viewed as just as important.

Credit: CommitStrip.com

The typical Drupal-based development workflow involves pushing code up from development to stage, and finally production, then pulling data downward from production into stage and development.

This means development and stage often have full copies of production. An attacker would not need to waste time targeting a heavily locked-down production instance if the same data was available on a much less secure development environment.

Blocking Access

One of the most basic ways to protect non-production environments is to simply block access to it completely. There are a few ways to achieve this, but one of the simplest is to rely on HTTP authentication (https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).

HTTP authentication can be achieved by editing the .htaccess file that ships with Drupal core. However the Shield module (https://www.drupal.org/project/shield) provides this functionality with a few advantages, including setting the configuration from within the Drupal UI and controlling the configuration from within PHP.

Most Drupal-based hosting options provide an environment variable that can be used to define different settings per environment. Acquia provides many examples here: (https://docs.acquia.com/article/password-protecting-nonproduction-environments).

Important Note:

Versions prior to 8.x-1.2 would display the username and password to the end user by default as part of its message. This should be changed in the configuration when setting the username and password. See https://www.drupal.org/project/shield/issues/2919568 for more information.

Sanitizing Data

A "best practice" is to ensure that any sensitive data only lives in production. This ensures non-production environments are not easy targets, but also protects against accidental information leaks from authorized users. Spamming your users with test emails from a development environment not only looks unprofessional, but could also put the users at risk.

Drush provides a method of cleaning up the user table with the built-in sql-sanitize command (https://drushcommands.com/drush-8x/sql/sql-sanitize/). The same logic runs when using sql-sync with the --sanitize flag. This command will sanitize a number of core's default fields for users. At a minimum it can anonymize all user email addresses and change their passwords. The Drupal 8 version will additionally anonymize other fields attached to the user entity.

This feature provides a great starting point, but it is not a magic bullet since Drush is unaware of the type of data a site may contain. Drush provides a hook to extend this default behavior and sanitize any other sensitive data in the database. A great example of this is the paranoia module (https://www.drupal.org/project/paranoia), which sanitizes additional items in the database (https://cgit.drupalcode.org/paranoia/tree/paranoia.drush.inc?h=7.x-1.x#n11). I recommend reviewing your application data structure to ensure any sensitive data is removed by adding a custom hook in the same way.

Ideally this should be automated to ensure sanitization is performed any time data is copied from production to another environment. Acquia provides cloud hooks to automate this step. In fact, if a project uses Acquia's BLT (https://github.com/acquia/blt), a cloud hook to run sql-sanitize is already provided (https://github.com/acquia/blt/blob/9.x/scripts/cloud-hooks/hooks/common/post-db-copy/db-scrub.sh).

Important Note: