Skip to content

Build1 publisher2 min readPublished

DB_PASS slips past the five keywords Spring Boot's env sanitizer matches on

The default masking on Spring Boot's /actuator/env tests the property name against five words, so a dev.to post's DB_PASS and WEBHOOK_SIGNING examples print in plaintext, and the custom pattern it proposes covers only those examples.

The Engineer · Build desk

Illustration accompanying DB_PASS slips past the five keywords Spring Boot's env sanitizer matches on

What happened

  • Spring Boot's /actuator/env sanitizer decides by property name: a key containing password, secret, key, token or credentials comes back as six asterisks, and other keys print their values.
  • The post lists DB_PASS, API_AUTH, WEBHOOK_SIGNING, PARTNER_SHARED_VALUE and INTERNAL_CIPHER as names carrying none of those words, all of which reach the JSON response with no asterisks.
  • Its fix is a SanitizingFunction bean whose case-insensitive pattern matches pass, auth, signing, shared or cipher in the key and returns six asterisks on a hit.
  • Spring Boot runs every registered SanitizingFunction in order, so the new bean joins the existing chain and does not replace the default one.
  • The author separates the widely repeated advice to set management.endpoint.env.show-values=when-authorized from the masking question, because it governs who sees values and not which values are masked.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure The reader of the plaintext is usually inside the perimeter: a monitoring service or ops dashboard that holds the authorized role and was never threat-modelled as a consumer of credentials.
  • constraint A keyword list can only cover names its author already remembered, and the proposed pattern has one alternative per example it was written against, so it needs re-auditing the next time a team invents a name.
  • contradiction The post's marquee example would be masked under the rule the post itself states, so the argument for a stricter default matcher rests on a framework version it never identifies.
  • cost Substring matching produces false positives: an operator debugging a BYPASS_CACHE flag gets asterisks back from the endpoint they turned on to read config.

Nothing in the default path looks at the value itself, only at the key carrying it. A token stored under INTERNAL_CIPHER is printed exactly as stored. The rule is a name test: password, secret, key, token or credentials in the key, and the value comes back as six asterisks [1][2]. Running Vault or AWS Secrets Manager in production does not change that, because the case the post describes is a staging YAML file that still holds the literal names [10].

The post's own counterexample works against it. THIRD_PARTY_SHARED_SECRET_VALUE contains the string "secret", so under the contains-rule the post itself describes, that value is already masked [14]. The post hedges, saying the default matcher may be stricter than a plain substring check for "secret" [10], and it names no version where that is true.

That gap runs through the piece. The author writes that SanitizingFunction replaced the keyword-based Sanitizer "in more recent versions of the framework" and stops there [6]. The keyword list, the management.endpoint.env.show-values property and the lambda signature all belong to specific releases. The snippet reads the key with data.getSanitizableData().getKey(), so the lambda parameter in that version is a wrapper, not the SanitizableData itself [13]. The post also never states what show-values defaults to, so how much of /actuator/env a fresh install returns to a curl is not established here [20].

The proposed pattern is (?i).*(pass|auth|signing|shared|cipher).* [11]. Five alternatives, five example names, one each: pass for DB_PASS, auth for API_AUTH, signing for WEBHOOK_SIGNING, shared for PARTNER_SHARED_VALUE, cipher for INTERNAL_CIPHER [15]. It closes exactly the holes the author had already written down. It also matches in the other direction, on any key containing "pass", so a BYPASS_CACHE flag comes back as asterisks too [17]. That is harmless right up to the afternoon someone is debugging why the cache is off.

"The default sanitizer covers the obvious names, but that's not where the real leaks happen," the author wrote [3]. The same argument went up twice on dev.to under the same author path, and the Spanish version words the thesis differently [5][18].

This transfers to your deployment only if the endpoint is reachable by a role you do not fully trust, and show-values is configured to expose values to that role [8]. It also takes one more condition: at least one secret in your config sits under a key that misses all five default words [16]. The first two are config lines you can read in a minute. The third is a list only your team has, and the post's answer is that the catalog of what to sanitize belongs to whoever configures the project [19].

What to watch

  • A Spring Boot release note that pins the version where SanitizingFunction replaced the keyword Sanitizer, and what show-values defaults to in it.
  • Whether the post revises the THIRD_PARTY_SHARED_SECRET_VALUE example, since contains-matching on "secret" would already mask it.
  • A second, independently authored write-up of the keyword list: both versions of this argument are currently the same author on dev.to.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories