12 points par darjeeling 2026-03-13 | Aucun commentaire pour le moment. | Partager sur WhatsApp

Résumé essentiel
Pour répondre aux attaques contre la chaîne d’approvisionnement visant l’écosystème des paquets Python, une stratégie de défense multicouche, qui ne repose pas sur un contrôle unique, est nécessaire. L’article recommande le blocage statique des vulnérabilités à l’aide des règles S (Bandit) de Ruff, le verrouillage des dépendances par hachage cryptographique avec uv, ainsi que l’exécution de pip-audit et la génération d’un SBOM (CycloneDX) dans l’environnement CI. Lors de la publication des paquets, il préconise l’adoption de Trusted Publishing fondé sur OIDC plutôt que de jetons API longue durée, et présente un pipeline opérationnel qui retarde volontairement l’introduction de nouveaux paquets (Delayed Ingestion) afin de laisser à la communauté le temps de les valider face au risque de paquets malveillants.

Analyse approfondie

  • Vecteurs d’attaque de la supply chain et dépendances transitives : PyPI compte aujourd’hui plus de 740 000 paquets, et de grands incidents de sécurité continuent de se produire, comme la prise de contrôle du domaine ctx, l’injection dans les scripts CI d’Ultralytics (YOLO) ou le vol de jetons GhostAction. Même lorsqu’un seul paquet est installé (par ex. Flask), de nombreuses dépendances transitives (par ex. MarkupSafe) sont également installées ; ainsi, même les paquets que les développeurs n’ont pas explicitement déclarés deviennent une vaste surface d’attaque.
  • Blocage préventif des vulnérabilités dans son propre code : avant même les paquets externes, il faut empêcher les défauts dans le code interne. Les secrets codés en dur, les algorithmes de hachage faibles (MD5, SHA1), les requêtes réseau sans timeout (sources de DoS) ou la sérialisation non sûre (pickle) passent facilement au travers des revues de code. Pour éviter cela, il est indispensable d’intégrer les règles de sécurité Bandit de Ruff dans les pipelines CI/CD et les IDE afin de détecter et bloquer automatiquement ces problèmes.
  • Verrouillage des dépendances et introduction différée (Delayed Ingestion) : lors de l’installation des dépendances, il ne suffit pas d’indiquer une version ; il faut aussi verrouiller le hachage cryptographique du paquet pour empêcher toute altération dans l’environnement d’exécution. En outre, pour réduire le risque avant qu’un nouveau paquet malveillant publié ne soit bloqué, il est pertinent d’utiliser le flag --exclude-newer de uv, ou de mettre en place dans un miroir interne une « file d’attente d’ingestion de 7 jours » afin de ne laisser entrer dans le réseau interne que les paquets ayant déjà fait l’objet d’une première validation par la communauté.
  • Publication sécurisée des paquets : les mainteneurs open source et éditeurs de paquets doivent abandonner les jetons API statiques, toujours exposés au risque de compromission, et passer à Trusted Publishing basé sur OIDC (OpenID Connect) avec Sigstore. Cela permet de générer automatiquement une attestation prouvant cryptographiquement le lien avec le dépôt source.

Principaux extraits de code et données

1. Vérifier les dépendances transitives

# Vérifier l’arbre de dépendances invisibles installées avec un seul paquet (Flask)  
uv pip install flask  
uv pip tree  
  
# Output:  
flask v3.1.0  
├── blinker v1.9.0  
├── click v8.1.8  
├── itsdangerous v2.2.0  
├── jinja2 v3.1.5  
│   └── markupsafe v3.0.2  
└── werkzeug v3.1.3  
    └── markupsafe v3.0.2  
  
2. Application d’un ensemble de règles de sécurité (Bandit) avec Ruff  
# Exemple de configuration `pyproject.toml`  
[tool.ruff]  
line-length = 120  
# Activer S (règles de sécurité Bandit) avec E (Error) et F (Pyflakes)  
lint.select = ["E", "F", "S"]  
  
# Exemples de motifs de vulnérabilité typiques détectés automatiquement par l’ensemble de règles 'S' de Ruff  
# FLAGGED: S301 - pickle.loads() présente un risque d’exécution de code arbitraire (`json.loads()` recommandé)  
import pickle  
data = pickle.loads(untrusted_input)  
  
# FLAGGED: S608 - vulnérabilité d’injection SQL via le formatage de chaîne  
cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")  
  
# FLAGGED: S113 - requête externe sans timeout défini (attente infinie et exposition aux attaques DoS)  
import requests  
response = requests.get("[https://api.example.com/data](https://api.example.com/data)")  
  
3. Contrôle des nouveaux paquets via l’introduction différée (Delayed Ingestion)  
# Compiler les dépendances en n’incluant que les paquets publiés avant une date donnée (par ex. il y a 7 jours) afin d’éviter les malwares/erreurs initiaux  
uv pip compile --exclude-newer 2026-03-02 requirements.in -o requirements.txt  
  
4. Pipeline de contrôle d’ingestion au sein de l’organisation  
| Étape de traitement | Composant système | Objectif de sécurité et explication |  
|---|---|---|  
| Entrée | PyPI ➜ Ingestion Queue | Les nouveaux paquets publiés sur PyPI ne sont pas déployés immédiatement dans le système interne, mais placés dans une file d’attente |  
| Attente | Wait (ex. 7 jours) | Obtenir un délai incompressible pour que la communauté et les chercheurs en sécurité analysent le caractère malveillant du paquet et ses vulnérabilités |  
| Validation | Security Scan ➜ Approved | À l’issue de la période d’attente, approbation uniquement si le paquet passe le scan des vulnérabilités connues (CVE) et des malwares |  
| Déploiement | Internal Mirror ➜ Developers | Seuls les paquets validés sont mis en cache dans le dépôt miroir interne et autorisés pour un usage sûr par les équipes de développement |

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.