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
+16-53Lines changed: 16 additions & 53 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,30 +16,23 @@ comments: true
16
16
last_modified: 2024-12-13
17
17
---
18
18
19
-
20
19
## Is your PyPI publication workflow secure?
21
20
22
-
The recent Python package breach [involving Ultralytics](https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection) has spotlighted the importance of securing your Python package PyPI publishing workflows. The Ultralytics breach was a supply chain attack where malicious actors exploited a GitHub workflow to inject harmful code into a Python package, enabling them to hijack users’ machines for Bitcoin mining. What this means in English:
23
-
24
-
> The Ultralytics breach happened when hackers tricked a Python package into running bad code, letting them use other people’s computers to mine Bitcoin without permission.
25
-
26
-
Yikes!
27
-
28
-
todo: just say no to bitcoin mining graphic
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.
29
22
23
+
> Hackers tricked a Python package into running bad code, using other people’s computers to mine Bitcoin without permission. Yikes!
30
24
31
-
As open source maintainers, it’s unsettling to consider that our package users could become vulnerable to breaches like this.
32
-
33
-
34
-
However, there’s a silver lining. In this case, the incredible PyPI security team had already addressed most of the issues that led to the Ultralytics breach. This incident highlights a gap in understanding of Python packaging security best practices.
25
+
While unsettling, there’s a silver lining: the PyPI security team had already addressed most of the issues that caused this breach.
35
26
36
27
37
28
{% include pyos-blockquote.html quote="Because the Ultralytics project was using Trusted Publishing and the PyPA’s publishing GitHub Action: PyPI staff, volunteers, and security researchers were able to dig into how maliciously injected software was able to make its way into the package." author="Seth Larson, PSF Security Expert" class="highlight magenta" %}
38
29
30
+
This incident underscores the importance of understanding Python packaging security best practices.
39
31
40
-
This makes sense--we love open source but can't be experts in everything.
32
+
In this blog post, we'll cover the lessons learned that you can apply - TODAY - to your own Python packaging workflows!
41
33
42
-
## Takeaways
34
+
<divclass="notice"markdown="1">
35
+
## TL&DR Takeaways
43
36
44
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:
45
38
@@ -57,28 +50,7 @@ The Ultralytics breach is a wake-up call for all maintainers: secure your workfl
57
50
- 📱 Enable 2FA for your PyPI account and store recovery codes securely.
58
51
59
52
These steps will significantly reduce risks to your packages, contributors, and the broader Python ecosystem. Don’t wait—start securing your workflows today.
60
-
61
-
### What happened in the Ultralytics breach?
62
-
63
-
The Ultralytics incident was a **supply chain attack**—a type of attack where sneaky coders compromise the tools or processes used to create or distribute software. In this case, the bad actors/hackers wanted to use the user's machines to mine Bitcoin. This was a hack with the goal of using other people's compute for illegal profit!
64
-
65
-
In this case:
66
-
67
-
- An attacker exploited a GitHub action's trigger (`pull_request_target`) to inject malicious dependencies into the project.
68
-
- This code was then published to PyPI
69
-
- When a user downloaded and installed the package, their local machine was compromised.
70
-
71
-
**Yikes!**
72
-
73
-
TODO: image meme of someone's head exploding
74
-
75
-
The root cause of the breach was actually:
76
-
77
-
* A GitHub action workflow configuration that granted publish permissions to pull requests, allowing the attacker to execute unauthorized actions.
78
-
* A leak of the repositories' PyPI token (likely through GitHub secrets).
79
-
* A user (or bot?) gained direct access to the repo itself to push changes to the build that had previously provided some level of security around who could kick off a build that was published to PyPI.
80
-
81
-
If you want more details about what happened, [you should check out Seth Larson's PyPI blog post on the event](https://blog.pypi.org/posts/2024-12-11-ultralytics-attack-analysis/), Seth is the resident security expert for the PSF and Python.
53
+
</div>
82
54
83
55
84
56
## A call to action if you are publishing to PyPI using GitHub actions
@@ -87,7 +59,7 @@ What's important for us is that this event highlights the need for our ecosystem
87
59
88
60
For this post, we will use [this workflow that pyOpenSci has setup](https://github.com/pyOpenSci/pyosMeta/blob/main/.github/workflows/publish-pypi.yml) that was reviewed and developed by both a PyPI maintainer and also several core pyOpenSci community members that have significant knowledge about best practices in publishing to PyPI.
89
61
90
-
Below, are actionable steps you can take to enhance security when publishing Python packages to PyPI using GitHub actions.
62
+
Below are actionable steps you can take to enhance security when publishing Python packages to PyPI using GitHub actions.
91
63
92
64
93
65
## 1. Avoid `pull_request_target` and consider release-based workflows
@@ -176,7 +148,6 @@ jobs:
176
148
...
177
149
```
178
150
179
-
180
151
## 4. Use Trusted Publisher for PyPI
181
152
182
153
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.
@@ -231,7 +202,7 @@ If you look at the pyometra workflow, notice that we have an [environment called
231
202
url: https://pypi.org/p/pyosmeta
232
203
```
233
204
234
-
## Sanitize a branch name in your workflow, before calling it!
205
+
## 6. Sanitize a branch name in your workflow, before calling it!
235
206
236
207
One of the critical issues in the Ultralytics breach was that attackers crafted a [**malicious branch name containing a shell script**](https://github.com/ultralytics/ultralytics/pull/18020) 🤯. This script executed within the GitHub Action workflow because the branch name was directly referenced using `github.ref`.
237
208
@@ -304,31 +275,22 @@ The good news here is that if you use a release-based workflow as discussed earl
304
275
305
276
Restricting publish workflows to tagged releases significantly reduces the risk of such attacks.
306
277
307
-
### Delete old tokens
308
-
309
-
If you are using a trusted publisher workflow but have previously created PyPI API tokens for your package to use in GitHub Actions, it’s time to clean house:
310
-
311
-
- Identify and revoke/delete any unused or old tokens in your PyPI account!!
312
-
- Do the same for your GitHub secrets!
313
-
- Migrate to Trusted Publisher workflows to avoid using tokens entirely (if you can).
314
278
315
279
## GitHub & PyPI security tips
316
280
317
-
The above tips are focused on your GitHub workflows. However, you can also consider locking down your accounts too!
318
-
319
-
* Make sure you have 2FA (2-factor authentication) setup for both PyPI and GitHub. This is common these days for financial and even social media accounts. Set things up for your tech and open source accounts too!
320
-
321
-
Important: Store recovery codes securely (e.g., a password manager).
322
-
323
-
* Be careful about who can gain direct write access to your project's repository. Only a specific, trusted subset of maintainers should be able to trigger a publish-to-PyPI workflow. Most contributors and maintainers don’t need direct write access to your repository; limiting access reduces security risks.
281
+
In addition to securing your workflows, lock down your accounts and repositories:
324
282
283
+
-**Enable 2FA (2-factor authentication)** for both PyPI and GitHub to prevent unauthorized access. Store recovery codes securely (e.g., in a password manager).
284
+
-**Revoke old tokens**: If you've previously created PyPI API tokens or GitHub secrets, delete any unused or outdated ones.
285
+
-**Restrict repository access**: Limit write access to a trusted subset of maintainers. Most contributors don’t need direct write access, which reduces security risks.
325
286
326
287
## Learn More
327
288
328
289
pyOpenSci follows best practices for PyPI publishing using our custom GitHub Actions workflow. Check out our tutorial on Python packaging here:
If you are on LinkedIn, you should [subscribe to our newsletter, too](https://www.linkedin.com/newsletters/7179551305344933888/?displayConfirmation=true).
0 commit comments