Back

Masking sensitive values

🧭

Is this package for you?

17 reasons, each a before and an after. Select the problems you have and see how many match.

Take the scorecard
🔒

Some values belong in code but not on screen

A payment webhook URL or a provisioned sender number is version-controlled configuration — but you still would not want it scrolling past in a CI log or sitting in a screenshot. Mark the property #[Sensitive] and the console prints ******** instead.

Nothing else changes. toArray() and your application still receive the real value.

Three rules, in order

1

Empty stays empty

A value that was never set prints as nothing. Masking it would imply a secret exists where none does.

2

Marked wins

A property carrying #[Sensitive] is masked whatever it holds — string, int, enum. The developer said so.

3

Then the name guess

Unmarked string properties whose name contains key, secret, password or token are masked as a fallback.

What actually happens

Six properties on one class, run through env-settings:show. Every row below is real command output.

Property Type Printed Why
#[Sensitive] $marked_int int ******** Marked — the attribute ignores type entirely.
$api_key string ******** Name contains "key". The fallback working as intended.
$max_tokens int 8000 Name contains "token", but it is an int — so it is shown.
$monkey_api_url string ******** "monkey" contains "key" — hidden for no reason.
#[Sensitive] $marked_empty string (blank) Empty even though marked. Rule 1 comes first.
$passphrase string swordfish Not one of the four fragments — printed in full.

⚠️ It over-matches

monkey_api_url contains key, so a perfectly public URL is hidden — and you are left wondering what the value was.

💥 It under-matches

passphrase, credentials and bearer are printed in full. This is the failure that matters.

✅

So mark the property

The name list is kept only so settings written before the attribute existed stay hidden. It is a guess, not a rule. One attribute removes the guesswork:

#[Sensitive] public string $passphrase,

In this demo

PaymentSettings::$webhook_url
| mode *           | sandbox  | live     |
| currency         | USD      | USD      |
| retry_attempts * | 1        | 5        |
| webhook_url *    | ******** | ******** |

A payment webhook URL usually embeds a token.

NotificationSettings::$sms_from
| sms_provider *          | log      | vonage   |
| sms_from *              | ******** | ******** |
| default_channel *       | log      | sms      |
| rate_limit_per_minute * | 100      | 200      |

A provisioned sender number is tied to the account.

🔀

Still flagged as differing

Both columns read ********, yet the row keeps its *. The diff compares the real values and masks only when printing, so you still learn that staging and production disagree — without learning what either one is.

📦

Display only

Masking lives in the commands, not the model. toArray(), serialisation and every line of your application still get the real value. Nothing breaks by marking a property.

Generate one already marked

env-settings:make can apply the attribute for you. Each name passed to --sensitive must also appear in --properties.

php artisan env-settings:make WebhookSettings \
  --properties="endpoint:string,signing_secret:string" \
  --sensitive="signing_secret"
Read the masking docs Enum-valued settings The completeness gate All four commands #[Environment] In tests Overrides Is this for you? Back to the demo