\Drupal\strata\Code SettingsRedactor

Strips the secrets out of `settings.php` before it is stored.

settings.php is worth backing up: it carries the trusted host patterns, the config sync directory, the reverse-proxy setup and every override a site depends on. It also carries the database password, the hash salt and whatever API keys the site put there, and those must not leave the server - a bucket someone can read is a bucket someone can read.

So the file is stored with every assignment to a known-secret key replaced, and the replacement is a marker rather than an empty string. A restore that wrote an empty password would produce a site that cannot connect to its own database and a stack trace that says nothing about why; a marker says exactly what happened and exactly what has to be filled in.

The redaction is by key name, and that is a decision with a limit. A secret in a key nobody anticipated is not caught. The list below is what Drupal and the common contributed modules actually use, and it errs wide: redacting a value that turns out not to be secret costs an operator one line to re-enter, while missing one puts a credential in an object store.

Summary

Methods
Properties
Constants
redact
isSecret
count
No public properties found
MARKER
SECRETS
No protected methods found
No protected properties found
No protected constants found
assignmentIn
redactLine
No private properties found
No private constants found

Constant

MARKER

MARKER = 'STRATA_REDACTED_SET_THIS_BEFORE_USE'

What a redacted value is replaced with.

Deliberately not empty and deliberately not valid: a restore has to fail loudly rather than come up with a blank password and an inscrutable error.

SECRETS

SECRETS = ['password', 'passwd', 'hash_salt', 'secret', 'private_key', 'api_key', 'apikey', 'access_key', 'secret_key', 'token', 'credential', 'salt', 'dsn', 'sentry', 'stripe', 'twilio', 'mailgun', 'sendgrid', 'smtp_pass', 'aws_secret']

Key fragments whose values are treated as secret, lowercased.

Matched as substrings of the assignment target, so $databases['default']['default']['password'] is caught by password, $settings['hash_salt'] by hash_salt, and the 'password' => line inside an array literal by the same fragment. The literal form is the one Drupal's own settings.php ships, so a rule that only matched variable paths would leave the database password in cleartext on every real site.

Methods

redact()

redact(string  $contents) : string

Redacts a settings file's contents.

Works line by line rather than by parsing PHP. A parser would understand the file better, and would also mean running the site's own configuration through an evaluator to back it up, which is a worse trade than being conservative about lines.

Parameters

string $contents

The file's contents.

Returns

string —

The contents with secret assignments replaced.

isSecret()

isSecret(string  $line) : bool

Whether a line assigns something this treats as secret.

Parameters

string $line

One line.

Returns

bool —

TRUE when the assignment target names a secret.

count()

count(string  $contents) : int

How many assignments in a file would be redacted.

Reported alongside a capture so an operator can see the file was handled rather than trusting that it was.

Parameters

string $contents

The file's contents.

Returns

int —

The count.

assignmentIn()

assignmentIn(string  $line) : array{int, string}|null

Where a line assigns, and with which operator.

=> is looked for first: inside an array literal it is the assignment, and reading its = as one would rewrite the line into something that no longer parses.

Parameters

string $line

One line.

Returns

array{int, string}|null —

The offset and the operator, or NULL when the line assigns nothing.

redactLine()

redactLine(string  $line) : string

Replaces the value in one line, keeping everything around it.

Parameters

string $line

One line.

Returns

string —

The line, redacted when it assigns a secret.