Skip to content

Cache keys are logged at Warning level; distributed-cache keys can be secrets (session ids) #129

Description

@cosmin-staicu

Problem

Cache keys are written to logs at LogLevel.Warning. For the application's own caches that is usually harmless — the keys are entity ids. For the IDistributedCache adapter added in #121 it is not: IDistributedCache keys are chosen by the consumer and can be secrets. Under ASP.NET Core session the key is the session identifier (the GUID SessionMiddleware decrypts from the client's cookie); rate limiters commonly key by API key or client IP.

The adapter fingerprints keys in its own two log messages, but it passes the composed CacheKey to the backing provider, which logs it. A Redis blip raises the inner-cache warning paths and writes session identifiers to the log — CWE-532. Warning is enabled in production, so this is not a debug-only exposure.

A logged session id is not by itself enough to hijack a session (forging the cookie needs the DataProtection key), but logs have a far wider audience and longer retention than the session store — aggregation platforms, support bundles, on-call access — and anyone with Redis read access, an RDB backup or a support dump goes from a logged id straight to that user's session contents.

Proposed fix

Add a per-cache switch, honored at every site that logs a key:

// ICacheOptions
bool LogKeys { get; }   // default true

Implemented as public bool LogKeys { get; set; } = true; on InMemoryCacheOptions, InMemoryRedisCacheOptions and RedisCacheOptions, surfaced through MultilayerCacheBase / RedisCacheBase, and consumed by a shared helper that renders either the key or a short SHA-256 digest — the same digest shape UiPathDistributedCache.Fingerprint already uses, so occurrences of one key stay correlatable in a log without revealing it. AddDistributedCache then sets LogKeys = false on the private provider's cloned options, alongside the CacheKeyStrategy neutralization it already does.

UiPathDistributedCache.Fingerprint should move to that shared helper rather than being duplicated.

Site inventory

Measured on feat/distributed-cache @ a7c0f6d — declarations taking CacheKey/RedisKey, and their call sites:

File LoggerMessage declarations Call sites
MultilayerCache.cs 15 48
MultilayerHashCache.cs 17 36
Redis/RedisHashCache.cs 3 6
Redis/RedisCache.cs 3 6
CacheEventPublisher.cs 1 (LogRaiseEvent)

The Warning-level ones — the production exposure — are MultilayerHashCache.LogInnerCacheRefreshError, LogInnerCacheContainsError, LogInnerCacheSetMetadataFailed, LogInnerCacheRemoveError, LogInnerCacheSetError, their MultilayerCache equivalents, and RedisHashCache/RedisCache LogLargeValueDetected.

Constraints

  • Must be applied to all of the above, not just the hash path. Honoring the flag in some classes and ignoring it in others reproduces the inert-setting defect class feat(cache): IDistributedCache adapter with case-sensitive keys and byte[] serialization #121 hit three times (AllowUnboundedEntries being ignored, CacheKeyStrategy unread by RedisHashCache, the tier Enabled switch checked too late).
  • Do not redact via CacheKey.ToString(). It looks like a one-line fix covering every current and future site, but CacheKey exposes an implicit operator string that is used functionally — PrefixRedisKeyStrategy.GetRedisKey composes the physical key with string.Join(Separator, Prefix, key) — so redacting either corrupts key composition or leaves ToString() and the implicit conversion disagreeing.
  • Keys stay verbatim in Redis, for parity with Microsoft.Extensions.Caching.StackExchangeRedis. This issue covers logs only; KEYS/SCAN output and RDB snapshots still contain them, which is documented in docs/reference/settings.md.

Interim state

#121 documents the exposure in docs/reference/settings.md under Caching:Distributed so an operator can scope log access or filter the UiPath.Caching categories until this lands.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions