Skip to content

Commit 5505fef

Browse files
committed
fix: edits from review
1 parent 23f55b1 commit 5505fef

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

_posts/2024-12-13-python-packaging-security.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ last_modified: 2024-12-13
1818

1919
## Is your PyPI publication workflow secure?
2020

21-
The recent Python package breach [involving Ultralytics](https://blog.pypi.org/posts/2024-12-11-ultralytics-attack-analysis/) has highlighted the need for secure PyPI publishing workflows. Hackers exploited a GitHub workflow (`pull_request_target`) to inject malicious code, which was published to PyPI. Users who downloaded the package unknowingly allowed their machines to be hijacked for Bitcoin mining.
21+
The recent Python package breach [involving Ultralytics](https://blog.pypi.org/posts/2024-12-11-ultralytics-attack-analysis/) has highlighted the need for secure PyPI publishing workflows. Hackers exploited a GitHub workflow to inject malicious code into their package. This package was then published to PyPI. Users who downloaded the package unknowingly allowed their machines to be hijacked for Bitcoin mining.
2222

2323
> Hackers tricked a Python package into running bad code, using other people’s computers to mine Bitcoin without permission. Yikes!
2424
@@ -36,18 +36,21 @@ In this blog post, we'll cover the lessons learned that you can apply - TODAY -
3636

3737
The Ultralytics breach is a wake-up call for all maintainers: secure your workflows now to protect your users and the Python ecosystem. Start with these key actions:
3838

39-
### 🔐 Secure your workflows 🔐
40-
- 🚫 Avoid risky events like `pull_request_target` and adopt release-based workflows.
39+
### 🔐 Secure your GitHub action workflows 🔐
40+
- 🚫 Avoid risky trigger events in GitHub actions like `pull_request_target`
41+
- Consider using publish workflows on GitHub that are triggered by a [GitHub release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) rather than a pull request merge.
4142
- ♻️ Don’t cache dependencies in your publish workflows to prevent tampering.
42-
- If you reference branches in a pull request, clean or sanitize branch names in your workflow.
43+
- If you reference branches in a pull request, clean or [sanitize branch names](https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names) in your workflow.
44+
- Consider using [zizmor](https://woodruffw.github.io/zizmor/) to check your GitHub workflows for security vulnerabilities!
4345

44-
### Lock down GitHub repo access
45-
- 🔒 Restrict repository access to essential maintainers only.
46-
- ✅ Add automated checks to ensure releases are authorized and secure.
46+
### Lock down both GitHub repo access and the workflow itself
47+
- 🔒 Restrict direct repository access to merge PRs and write to the repository to your core maintainers.
48+
- ✅ Use an environment in your GitHub workflow that creates a secure connection with PyPI.
49+
- Delete old GitHub tokens that you are no longer using. And Refresh existing tokens that you need periodically.
4750

4851
### Strengthen PyPI security
4952
- 🔑 Set up Trusted Publisher for tokenless authentication with PyPI.
50-
- 📱 Enable 2FA for your PyPI account and store recovery codes securely.
53+
- Make sure you store recovery codes securely for PyPI 2-factor authentication (2FA).
5154

5255
These steps will significantly reduce risks to your packages, contributors, and the broader Python ecosystem. Don’t wait—start securing your workflows today.
5356
</div>
@@ -153,18 +156,21 @@ jobs:
153156
If you only [publish locally to PyPI using the command line](https://www.pyopensci.org/python-package-guide/tutorials/publish-pypi.html), you need to use a PyPI token. However, if you’re using GitHub Actions to automate your publishing process, setting up **Trusted Publisher** is a more secure option.
154157

155158
A Trusted Publisher setup creates a secure "pipeline" between PyPI and your GitHub repository because:
156-
- PyPI is allowed to authenticate your builds directly, so no additional configuration is required.
159+
- PyPI is allowed to authenticate your [package distribution files (sDist and Wheel archives)](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-distribution-files-sdist-wheel.html#how-to-create-the-distribution-format-that-pypi-and-pip-expects) uploads directly, so no additional configuration is required.
157160
- It restricts publishing to specific workflows and environments defined in your repository.
158161

159-
This setup eliminates the need to store sensitive tokens as GitHub secrets, significantly reducing the risk of token theft or unauthorized publication.
162+
TODO: fix
163+
This setup eliminates the need to store sensitive tokens as GitHub secrets. And if used with GitHub environments that only can be run by a "trusted human" manually authorizing the action to publish, ... it will significantly reduce the risk of token theft or unauthorized publication.
164+
165+
NOTE: vI don't fully agree with that note on unauthorized publication. Unauthorized publication can be prevented by controlling the trigger of the release process. Or setting up required reviewers in the environment used in the publishing job (usually called pypi) — this would prevent said job from even starting until a trusted human being clicks a button on GH. Recording the environment name on the PyPI side, though, allows restricting the “identity” of the workflow+repo+env that is to be considered “trusted”. But this only works on the authentication level between PyPI and GH. But not between GH and human, which is what you also want to secure.
160166

161167
### How to get started
162168

163169
[PyPI provides a great guide to getting started with Trusted Publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/).
164170

165171
The basic steps associated with Trusted Publisher are:
166172
1. Go to your PyPI account and add a trusted publisher workflow to your account.
167-
2. Fill out a form that looks like the one below. Notice that it asks for your workflow name, (optional) environment, and package name.
173+
2. Fill out a form that looks like the one below. Notice that it asks for your workflow name, environment (STRONGLY recommended), and package name.
168174
3. Update your GitHub action workflow to reference the Trusted Publisher configuration.
169175

170176
<figure>
@@ -179,15 +185,15 @@ The basic steps associated with Trusted Publisher are:
179185

180186
You can see how to set up GitHub Actions securely in our own [PyPI publishing GitHub workflow](https://github.com/pyOpenSci/pyosMeta/blob/main/.github/workflows/publish-pypi.yml), which follows the Trusted Publisher approach.
181187

182-
**Note:** Trusted Publisher workflows are currently only available for GitHub. Support for GitLab may be coming in the future—stay tuned!
188+
**Note:** Read more here about [support for publishing to GitLab](https://docs.pypi.org/trusted-publishers/adding-a-publisher/#gitlab-cicd) using trusted publishing.
183189
{: .notice }
184190

185-
## 5. Create a dedicated environment for publish actions
191+
## 5. Create a dedicated environment for publishing actions
186192

187193
Use isolated environments in combination with Trusted Publisher in your GitHub workflow to publish to PyPI.
188194
Isolated environments ensure your publishing process remains secure even if other parts of your CI pipeline are compromised.
189195

190-
If you look at the pyometra workflow, notice that we have an [environment called `pypi`](https://github.com/pyOpenSci/pyosMeta/blob/main/.github/workflows/publish-pypi.yml#L57) that is used for trusted publishing. By setting this up, we have created a direct pipeline between this action and PyPI via the PyPI environment and the trusted publisher setup, which refers to the workflow file's name.
196+
If you look at the pyoMeta workflow, notice that we have an [environment called `pypi`](https://github.com/pyOpenSci/pyosMeta/blob/2a09fba/.github/workflows/publish-pypi.yml#L57) that is used for trusted publishing. By setting this up, we have created a direct pipeline between this action and PyPI via the PyPI environment and the trusted publisher setup, which refers to the workflow file's name.
191197

192198
```yaml
193199
publish:
@@ -201,6 +207,7 @@ If you look at the pyometra workflow, notice that we have an [environment called
201207
name: pypi
202208
url: https://pypi.org/p/pyosmeta
203209
```
210+
<TODO: add an image of setting up permissions in the REPO for who can authorize this environment to run>
204211
205212
## 6. Sanitize a branch name in your workflow, before calling it!
206213

0 commit comments

Comments
 (0)