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
Summary
On Unix,
dotnet user-jwtswrites its signing-key-bearingsecrets.jsonwithout enforcing owner-only file permissions. The resulting mode depends on the process umask and can be broader than the0600invariant used by related user-secrets files.What is wrong
SigningKeysHandler.CreateSigningKeyMaterialopenssecrets.jsonwithFileMode.Createand does not request or normalize its Unix mode.JwtStore.SaveandSecretsStore.Saveenforce owner-only access, but the signing-key writer does not.FileStreamOptions.UnixCreateModealone 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
0600aligns 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-98creates and rewrites the signing-key file.src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs:253creates a key when none exists.src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs:78resets an existing key.src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs:1024-1037checks the mode ofuser-jwts.jsonbut notsecrets.json.Recommended fix
Open the key file with
FileMode.OpenOrCreate. On non-Windows systems, requestUnixFileMode.UserRead | UnixFileMode.UserWritethroughUnixCreateMode, then apply the same mode withFile.SetUnixFileModeusing the opened stream'sSafeFileHandlebefore truncating and serializing. This protects new files and normalizes existing permissive files without a path-based mode-change race.Using only
UnixCreateModeis 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 with0600to 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
secrets.jsonhas exactly0600permissions on Unix under a typical0022umask.0644signing-key file changes its mode to exactly0600.secrets.jsonanduser-jwts.json.