Skip to content

Restrict dotnet user-jwts signing-key file permissions on Unix #69320

Description

@rokonec

Summary

On Unix, dotnet user-jwts writes its signing-key-bearing secrets.json without enforcing owner-only file permissions. The resulting mode depends on the process umask and can be broader than the 0600 invariant used by related user-secrets files.

What is wrong

  • SigningKeysHandler.CreateSigningKeyMaterial opens secrets.json with FileMode.Create and does not request or normalize its Unix mode.
  • The file contains the key used to sign local development JWTs.
  • Related writers in JwtStore.Save and SecretsStore.Save enforce owner-only access, but the signing-key writer does not.
  • FileStreamOptions.UnixCreateMode alone is insufficient because it applies only when creating a new file. Existing files must also be normalized when rewritten.

Why it matters (defense in depth)

The signing key is per-user secret material and should remain isolated to the owning user account. Enforcing 0600 aligns this writer with the documented safe user-profile storage behavior and with the existing user-secrets and user-jwts file protections.

This is low-severity defense-in-depth hardening for a local development tool. Servicing fixes are not planned.

Affected code

  • src/Tools/dotnet-user-jwts/src/Helpers/SigningKeysHandler.cs:58-98 creates and rewrites the signing-key file.
  • src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs:253 creates a key when none exists.
  • src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs:78 resets an existing key.
  • src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs:1024-1037 checks the mode of user-jwts.json but not secrets.json.

Recommended fix

Open the key file with FileMode.OpenOrCreate. On non-Windows systems, request UnixFileMode.UserRead | UnixFileMode.UserWrite through UnixCreateMode, then apply the same mode with File.SetUnixFileMode using the opened stream's SafeFileHandle before truncating and serializing. This protects new files and normalizes existing permissive files without a path-based mode-change race.

Using only UnixCreateMode is not sufficient because it does not change an existing file. Temporary-file replacement is also viable, but any temporary file should be created in the destination directory with 0600 to avoid cross-file-system moves.

Apply the change to main. The change has no public API or JSON-format impact. Windows behavior remains unchanged. On Unix, an existing file with broader permissions is tightened on the next key creation or reset.

Acceptance criteria

  • A newly created signing-key secrets.json has exactly 0600 permissions on Unix under a typical 0022 umask.
  • Rewriting an existing 0644 signing-key file changes its mode to exactly 0600.
  • Existing unrelated user-secrets values remain intact after the rewrite.
  • Unix regression tests cover both secrets.json and user-jwts.json.
  • Windows behavior and the persisted JSON format remain unchanged.

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

    area-commandlinetoolsIncludes: Command line tools, dotnet-dev-certs, dotnet-user-jwts, and OpenAPIfeature-userjwtsThe `dotnet user-jwts` CLI tool

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions