Skip to content

Windows: a store for service "MyApp" enumerates, loads and deletes the credentials of service "MyApp:Dev", because target names are joined with ':' unescaped #176

Description

@matt-edmondson

What's wrong

WindowsCredentialStore builds the Credential Manager target name as $"{_servicePrefix}:{persona}" (CredentialCache/Storage/WindowsCredentialStore.cs:32). EnumerateKeys (lines ~130-157) filters with CredEnumerate("{prefix}:*") and treats everything after "{prefix}:" as the persona.

Nothing stops the service name from containing :. CredentialStoreFactory.CreateDefault(serviceName) accepts any non-empty name and encourages per-app names. Two services where one name is a :-prefix of the other therefore share a namespace.

On Linux and macOS, service and account are separate attributes, so this only affects Windows.

Failure scenario

App A uses service "MyApp" and App B (or a dev build) uses "MyApp:Dev". B saves a persona abcd…, stored as target MyApp:Dev:abcd….

  • A's EnumerateKeys() matches MyApp:* and returns a persona "Dev:abcd…".
  • A's TryLoad of that persona rebuilds MyApp:Dev:abcd… and returns B's credential.
  • A's Remove of that persona deletes B's credential.

So a "remove all my personas" loop in A wipes B's stored credentials.

This was found by tracing the code. It was not run, because no Windows host was available. The path is deterministic string handling.

Suggested fix

Any one of these:

  • Reject : and * in the Windows service prefix in the constructor or factory.
  • In EnumerateKeys, skip any remainder that is not a valid persona GUID or that still contains :.
  • Escape the service component when building the target name, or record the service in the credential's UserName/attributes and filter on that.

Acceptance criteria

  • A store for "MyApp" never enumerates, loads or removes a credential written by a store for "MyApp:Dev".
  • A test covers the prefix-collision case. It can be Windows-only, or a pure test of the target-name and enumeration-filter logic if that is factored out.

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions