You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _posts/2024-12-13-python-packaging-security.md
+21-14Lines changed: 21 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ last_modified: 2024-12-13
18
18
19
19
## Is your PyPI publication workflow secure?
20
20
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.
22
22
23
23
> Hackers tricked a Python package into running bad code, using other people’s computers to mine Bitcoin without permission. Yikes!
24
24
@@ -36,18 +36,21 @@ In this blog post, we'll cover the lessons learned that you can apply - TODAY -
36
36
37
37
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:
38
38
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.
41
42
- ♻️ 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!
43
45
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.
47
50
48
51
### Strengthen PyPI security
49
52
- 🔑 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).
51
54
52
55
These steps will significantly reduce risks to your packages, contributors, and the broader Python ecosystem. Don’t wait—start securing your workflows today.
53
56
</div>
@@ -153,18 +156,21 @@ jobs:
153
156
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.
154
157
155
158
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.
157
160
- It restricts publishing to specific workflows and environments defined in your repository.
158
161
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.
160
166
161
167
### How to get started
162
168
163
169
[PyPI provides a great guide to getting started with Trusted Publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/).
164
170
165
171
The basic steps associated with Trusted Publisher are:
166
172
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.
168
174
3. Update your GitHub action workflow to reference the Trusted Publisher configuration.
169
175
170
176
<figure>
@@ -179,15 +185,15 @@ The basic steps associated with Trusted Publisher are:
179
185
180
186
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.
181
187
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.
183
189
{: .notice }
184
190
185
-
## 5. Create a dedicated environment for publish actions
191
+
## 5. Create a dedicated environment for publishing actions
186
192
187
193
Use isolated environments in combination with Trusted Publisher in your GitHub workflow to publish to PyPI.
188
194
Isolated environments ensure your publishing process remains secure even if other parts of your CI pipeline are compromised.
189
195
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.
191
197
192
198
```yaml
193
199
publish:
@@ -201,6 +207,7 @@ If you look at the pyometra workflow, notice that we have an [environment called
201
207
name: pypi
202
208
url: https://pypi.org/p/pyosmeta
203
209
```
210
+
<TODO: add an image of setting up permissions in the REPO for who can authorize this environment to run>
204
211
205
212
## 6. Sanitize a branch name in your workflow, before calling it!
0 commit comments