Skip to content

Harden cookie file permissions in CookieJar.save() - #12312

Merged
Dreamsorcerer merged 5 commits into
aio-libs:masterfrom
digiscrypt:secure-cookiejar-file-permissions
Apr 6, 2026
Merged

Harden cookie file permissions in CookieJar.save()#12312
Dreamsorcerer merged 5 commits into
aio-libs:masterfrom
digiscrypt:secure-cookiejar-file-permissions

Conversation

@digiscrypt

Copy link
Copy Markdown
Contributor

This change hardens cookie persistence file permissions in CookieJar.save().

Problem
Cookie files may be created with overly permissive permissions depending on the system umask. Since these files can contain authentication and session tokens, this creates a risk of unintended exposure.

Risk
On shared systems, other local users may be able to read persisted cookies and reuse session data.

Fix
New files are now created using os.open with O_CREAT | O_EXCL and mode 0600, ensuring least-privilege permissions at creation time.

If the file already exists, it is reopened for overwrite and its permissions are tightened to 0600 on POSIX systems (using fchmod where available, with a fallback to chmod).

Why this matters
This reduces the risk of leaking sensitive authentication data at rest and makes cookie persistence safer by default.

Testing
Added tests to verify:

  • new files are created with 0600 permissions
  • existing files with broader permissions (e.g. 0644) are tightened to 0600

Permission checks use stat.S_IMODE and are skipped on unsupported platforms.

Test status
Focused tests pass:

  • test_save_creates_private_cookie_file
  • test_save_tightens_existing_cookie_file_permissions
  • test_save_load
  • test_save_load_json_secure_cookies

@digiscrypt
digiscrypt requested a review from asvetlov as a code owner April 3, 2026 05:10
Comment thread tests/test_cookiejar.py Outdated
@digiscrypt

Copy link
Copy Markdown
Contributor Author

I updated the patch so permissions are enforced only when the cookie file is first created (0600 via os.open + O_CREAT | O_EXCL).
For existing files, save now preserves the current mode and only overwrites content.
I also updated tests to match this behavior: new file gets 0600, existing 0644 remains 0644.

Comment thread aiohttp/cookiejar.py Outdated
@digiscrypt

Copy link
Copy Markdown
Contributor Author

I simplified this further.
The save path now uses a single open call with an opener that passes mode 0600 to os.open. That keeps new-file creation private while preserving existing file permissions on overwrite

Comment thread aiohttp/cookiejar.py
@Dreamsorcerer Dreamsorcerer added backport-3.13 backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot labels Apr 6, 2026
@Dreamsorcerer
Dreamsorcerer requested a review from webknjaz as a code owner April 6, 2026 19:55
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Apr 6, 2026
@codecov

codecov Bot commented Apr 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.11%. Comparing base (d9bd1be) to head (45111dd).
⚠️ Report is 514 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #12312   +/-   ##
=======================================
  Coverage   99.11%   99.11%           
=======================================
  Files         130      130           
  Lines       45558    45608   +50     
  Branches     2404     2405    +1     
=======================================
+ Hits        45155    45205   +50     
  Misses        272      272           
  Partials      131      131           
Flag Coverage Δ
CI-GHA 98.97% <100.00%> (+<0.01%) ⬆️
OS-Linux 98.71% <100.00%> (+<0.01%) ⬆️
OS-Windows 96.96% <38.09%> (-0.03%) ⬇️
OS-macOS 97.88% <100.00%> (+0.01%) ⬆️
Py-3.10.11 97.42% <100.00%> (+<0.01%) ⬆️
Py-3.10.20 97.89% <100.00%> (+<0.01%) ⬆️
Py-3.11.15 98.10% <100.00%> (-0.01%) ⬇️
Py-3.11.9 97.63% <100.00%> (+<0.01%) ⬆️
Py-3.12.10 97.72% <100.00%> (+<0.01%) ⬆️
Py-3.12.13 98.20% <100.00%> (+<0.01%) ⬆️
Py-3.13.12 98.45% <100.00%> (+<0.01%) ⬆️
Py-3.14.3 98.50% <100.00%> (+<0.01%) ⬆️
Py-3.14.3t 97.50% <100.00%> (-0.01%) ⬇️
Py-pypy3.11.15-7.3.21 97.38% <100.00%> (+<0.01%) ⬆️
VM-macos 97.88% <100.00%> (+0.01%) ⬆️
VM-ubuntu 98.71% <100.00%> (+<0.01%) ⬆️
VM-windows 96.96% <38.09%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@codspeed-hq

codspeed-hq Bot commented Apr 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 59 untouched benchmarks


Comparing digiscrypt:secure-cookiejar-file-permissions (45111dd) with master (f4dc0de)

Open in CodSpeed

@Dreamsorcerer
Dreamsorcerer merged commit 47558a3 into aio-libs:master Apr 6, 2026
44 checks passed
@patchback

patchback Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Backport to 3.14: 💔 cherry-picking failed — conflicts found

❌ Failed to cleanly apply 47558a3 on top of patchback/backports/3.14/47558a30c88e31cc3b5ff3b57f6127e7984c80bc/pr-12312

Backporting merged PR #12312 into master

  1. Ensure you have a local repo clone of your fork. Unless you cloned it
    from the upstream, this would be your origin remote.
  2. Make sure you have an upstream repo added as a remote too. In these
    instructions you'll refer to it by the name upstream. If you don't
    have it, here's how you can add it:
    $ git remote add upstream https://github.com/aio-libs/aiohttp.git
  3. Ensure you have the latest copy of upstream and prepare a branch
    that will hold the backported code:
    $ git fetch upstream
    $ git checkout -b patchback/backports/3.14/47558a30c88e31cc3b5ff3b57f6127e7984c80bc/pr-12312 upstream/3.14
  4. Now, cherry-pick PR Harden cookie file permissions in CookieJar.save() #12312 contents into that branch:
    $ git cherry-pick -x 47558a30c88e31cc3b5ff3b57f6127e7984c80bc
    If it'll yell at you with something like fatal: Commit 47558a30c88e31cc3b5ff3b57f6127e7984c80bc is a merge but no -m option was given., add -m 1 as follows instead:
    $ git cherry-pick -m1 -x 47558a30c88e31cc3b5ff3b57f6127e7984c80bc
  5. At this point, you'll probably encounter some merge conflicts. You must
    resolve them in to preserve the patch from PR Harden cookie file permissions in CookieJar.save() #12312 as close to the
    original as possible.
  6. Push this branch to your fork on GitHub:
    $ git push origin patchback/backports/3.14/47558a30c88e31cc3b5ff3b57f6127e7984c80bc/pr-12312
  7. Create a PR, ensure that the CI is green. If it's not — update it so that
    the tests and any other checks pass. This is it!
    Now relax and wait for the maintainers to process your pull request
    when they have some cycles to do reviews. Don't worry — they'll tell you if
    any improvements are necessary when the time comes!

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

@aio-libs aio-libs deleted a comment from patchback Bot Apr 6, 2026
@Dreamsorcerer

Copy link
Copy Markdown
Member

Could you handle the 3.14 backport following the above instructions?

digiscrypt added a commit to digiscrypt/aiohttp that referenced this pull request Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants