security: create key files with 0o600 permissions atomically#495
Open
OscarOzaine wants to merge 1 commit into
Open
security: create key files with 0o600 permissions atomically#495OscarOzaine wants to merge 1 commit into
OscarOzaine wants to merge 1 commit into
Conversation
CBORSerializable.save() previously used a plain open(path, "w"), so key files inherited the process umask (world-readable under the common 0o022) and the os.path.isfile existence check was a TOCTOU race that also followed symlinks. save() now creates the file with os.open(O_WRONLY|O_CREAT|O_EXCL, mode) followed by an explicit chmod, defaulting to 0o600 (owner read/write only). O_EXCL makes creation atomic, closing the TOCTOU race and refusing to follow or overwrite an existing path; FileExistsError is translated back to the original IOError. A new mode keyword lets non-secret callers opt into wider permissions. POSIX modes are advisory on Windows, which is documented. Note: an empty pre-existing target is now also rejected (O_EXCL tightening), where the old isfile/st_size check allowed it. Tests that pre-created temp files via NamedTemporaryFile are migrated to pytest's tmp_path fixture. Implements docs/specs/spec-02-key-file-permissions.md (PR 1 / P0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
CBORSerializable.save()(used to write.skeykey files) had three weaknesses:0o644), so other local users could read your secret keys.The fix
save()now creates the file atomically withos.open(path, O_WRONLY | O_CREAT | O_EXCL, mode):O_EXCLmakes creation atomic — it fails if the path already exists (closing the race) and refuses to follow a pre-existing symlink.chmod-ed to the requested mode regardless of umask.0o600(owner read/write only).A new
modekeyword lets callers of non-secret objects opt into wider permissions when they need it.Behavior changes to be aware of
save()now refuses to overwrite any existing file (previously it only rejected non-empty files — an empty pre-existing file used to be allowed).Tests
All 574 tests pass. Tests were migrated from
NamedTemporaryFileto pytest'stmp_path(so they don't pre-create the target file, which the newO_EXCLbehavior rejects). New tests cover the0o600mode, overwrite refusal, and partial-write cleanup.