Skip to content

fix(auth): [Security] Avoid writing session credentials to the platform log & mask sensitive values - #1391

Open
hieuwu wants to merge 2 commits into
supabase-community:masterfrom
hieuwu:fix/mask-session-tokens
Open

fix(auth): [Security] Avoid writing session credentials to the platform log & mask sensitive values#1391
hieuwu wants to merge 2 commits into
supabase-community:masterfrom
hieuwu:fix/mask-session-tokens

Conversation

@hieuwu

@hieuwu hieuwu commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Stop writing session credentials to the platform log

What is the current behavior?

UserSession is a data class holding four bearer credentials as plain Strings, so the compiler-synthesized toString() prints all four verbatim. Four call sites interpolated credential-bearing values straight into log lines:

# Location Leaked value
1 AuthImpl.kt:569 logger.d { "Importing session $session from $source, ..." } accessToken, refreshToken, providerToken, providerRefreshToken via $session $source. As SessionSource.Refresh / UserChanged / UserIdentitiesChanged wrap a UserSession
2 AuthExtensions.kt:27 logger.d { "Fragment parts: $sessionParts" } The parsed OAuth redirect fragment map, which by construction holds access_token and refresh_token
3 url/HashParsing.kt:9 logger.d { "Parsing fragment/hash $hash" } The raw OAuth redirect fragment, credentials included
4 Utils.kt:30 logger.e(e) { "Couldn't retrieve user using access token $accessToken..." } A raw access token at ERROR, which is enabled under the shipped default log level

Please link any relevant issues here.

What is the new behavior?

Fixed at the type as root, not at the call sites. New data class that if wraps a UserSession will not redintroduce the leak

1. Mask at the type boundary. UserSession overrides toString() to render every credential through StringMasking.maskString, keeping a two-character prefix and the length so the value stays debuggable.

SessionSource.Refresh / UserChanged / UserIdentitiesChanged are data classes wrapping a UserSession, their toString() delegates to the overridden one

2. Mask the OAuth redirect fragment. Not related to UserSession, two new helpers in StringMasking to cover more sensitive parameters

  • maskParameters(Map<String, String>) for a parsed map
  • maskParameterString(String) for a raw a=b&c=d fragment or query string.
    Both mask a denylist of credential parameter names (access_token, refresh_token, provider_token, provider_refresh_token, id_token, code, code_verifier) and leave everything else readable, so expires_in=3600&token_type=bearer still shows.
    maskParameterString preserves ordering and passes malformed segments through unchanged, so the output stays useful for diagnosing a bad redirect.

3. Header masking is now case-insensitive and wider. SENSITIVE_HEADERS became a lowercase Set matched against key.lowercase(), extended with cookie, set-cookie, proxy-authorization and x-api-key. Bearer-prefix handling is driven by the value (startsWith("Bearer ")) rather than an exact-case key comparison, and every value of a multi-valued header is masked rather than just the first.

Additional context

References and practices.

  • Domain primitives: a value with a security invariant enforces that invariant itself rather than delegating it to every consumer, and toString() is the exact seam where it belongs, because that is what string interpolation calls. Johnsson, Deogun & Sawano, Secure by Design (Manning, 2019), Ch. 5; Ch. 9 ("Handling failures securely") makes the narrower point that log and exception output must not carry the secret that caused the failure.

  • RFC 6749 §10.3 — access token credentials must be kept confidential in transit and
    storage
    , and the section explicitly notes that the implicit grant exposes the token in the
    URI fragment. A platform log is storage; this is the control the four call sites broke.
    https://www.rfc-editor.org/rfc/rfc6749#section-10.3

  • OWASP MASVS-STORAGE-2 "The app prevents leakage of sensitive data" (OWASP MASVS v2),
    naming logs as an unintentional leakage sink, with MASWE-0005: Insertion of Sensitive Data
    into Logs
    as the mapped weakness.
    https://mas.owasp.org/MASVS/controls/MASVS-STORAGE-2/

  • OWASP ASVS 4.0.3 §7.1.1 the application must not log credentials; ASVS maps this
    requirement to CWE-532. Masking here reduces exposure consistent with the intent of 7.1.1;
    the control's own remedy for session tokens is an irreversible hashed form, which a
    prefix-plus-length rendering is not.

  • CWE-532 Insertion of Sensitive Information into Log File.
    https://cwe.mitre.org/data/definitions/532.html

  • RFC 6749 §4.2.2 defines the implicit-grant redirect fragment and its parameters
    (access_token, token_type, expires_in, refresh_token, state), delivered in
    application/x-www-form-urlencoded form. This is the parameter set SENSITIVE_PARAMETERS
    is drawn from. https://www.rfc-editor.org/rfc/rfc6749#section-4.2.2

@hieuwu hieuwu changed the title fix(auth): Stop writing session credentials to the platform log fix(auth): Security-Stop writing session credentials to the platform log Sep 6, 2026
@hieuwu hieuwu changed the title fix(auth): Security-Stop writing session credentials to the platform log fix(auth): [Security] Avoid writing session credentials to the platform log & mask sensitive values Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant