diff --git a/content/library/application-security/recommendations/managing-dependency-threats.md b/content/library/application-security/recommendations/managing-dependency-threats.md index 90c9a4f..8f3623c 100644 --- a/content/library/application-security/recommendations/managing-dependency-threats.md +++ b/content/library/application-security/recommendations/managing-dependency-threats.md @@ -65,33 +65,34 @@ github: ## Scenario overview -Modern software development relies heavily on dependencies - third-party packages, libraries, and modules that accelerate development but also introduce security risks. Supply chain attacks targeting dependencies have become increasingly sophisticated, with malicious actors using techniques like typosquatting, package hijacking, account takeovers, and injecting malicious code into legitimate packages. +Modern software development relies heavily on dependencies - third-party packages, libraries, and modules that accelerate development but also introduce security risks. Supply chain attacks targeting dependencies have become increasingly sophisticated, with malicious actors using techniques like typosquatting, package confusion, package hijacking, account takeovers, and injecting malicious code into legitimate packages. One example is [Shai-Hulud](https://socket.dev/blog/ongoing-supply-chain-attack-targets-crowdstrike-npm-packages), a self-replicating worm that infiltrated the npm ecosystem via compromised maintainer accounts, injecting malicious post-install scripts into popular JavaScript packages. The malware also stole credentials, injected backdoors, and attempted to destroy the user's home directory. -Managing dependency threats requires a defense-in-depth strategy. No single control is perfect, so layering multiple defenses creates a more robust security posture. This article provides opinionated guidance on implementing practical security measures that have been field-tested against real-world attacks like Shai-Hulud. +Managing dependency threats requires a defense-in-depth strategy. No single control is perfect. Even registry-level protections like package cooldown periods (waiting periods after unpublishing before names can be re-registered) aren't sufficient against determined attackers. Layering multiple defenses creates a more robust security posture. This article provides opinionated guidance on implementing practical security measures that have been field-tested against real-world attacks like Shai-Hulud. ## Key design strategies Layered defenses reduce single points of failure by using multiple controls that reinforce each other - each covers gaps the others leave, so together they form a complete defense. Implement these core strategies: 1. **Disable package lifecycle scripts by default**: Package managers execute scripts automatically during installation (`preinstall`, `postinstall`, etc.). These scripts can run arbitrary code on your system before you've reviewed the package contents. Disabling them by default prevents the most common attack vector. -2. **Use dev containers for isolation**: Development containers provide strong isolation between host machine and project environment. Even if malicious code executes, it can't access the actual home directory, SSH keys, or cloud credentials. GitHub Codespaces and Microsoft Dev Box provide managed environments with this isolation built in. +2. **Use dev containers for isolation**: Development containers provide strong isolation between host machine and project environment. Even if malicious code executes, it can't access the actual home directory, SSH keys, or cloud credentials. GitHub Codespaces and Windows 365 provide managed environments with isolation built in. 3. **Require signed commits with user interaction**: Cryptographic commit signing with biometric or password authentication prevents malicious scripts from creating commits without your knowledge. This blocks multi-stage attacks that modify your codebase. -4. **Enforce repository rulesets**: Require pull requests and status checks for all changes to protected branches. This creates a checkpoint where automated security scans catch malicious code before it reaches your main branch. +4. **Enforce repository rulesets**: Require pull requests and status checks for all changes to protected branches. This creates a checkpoint where automated security scans catch malicious code before it reaches your default branch. 5. **Establish trusted publishing and verification**: Implement OIDC-based trusted publishing to eliminate long-lived tokens, and validate package attestations when consuming dependencies to verify they come from trusted sources. 6. **Monitor and respond continuously**: Automate vulnerability detection with Dependabot, dependency review, code scanning, and secret scanning. Tune alerts to reduce fatigue and establish response runbooks. **Implementation checklist:** - [ ] Configure package managers to disable lifecycle scripts by default +- [ ] Always use lockfiles for ecosystems that support them to ensure deterministic builds - [ ] Use dev containers for development, especially for untrusted code - [ ] Enable commit signing with user interaction (passphrase, biometric, or hardware key) - [ ] Configure repository rulesets requiring pull requests and status checks - [ ] Enable Dependabot alerts and security updates for all repositories - [ ] Configure dependency review action as a required status check on pull requests -- [ ] Enable code scanning with CodeQL or third-party tools, blocking high-severity issues -- [ ] Enable secret scanning with push protection active +- [ ] Enable code scanning with CodeQL or third-party tools; block merges on high-severity findings using rulesets +- [ ] Enable secret scanning with push protection; verify that partner patterns are enabled and custom patterns are defined for organization-specific secrets - [ ] Use trusted publishing with OIDC for any packages you maintain - [ ] Publish packages with provenance attestations (e.g., npm publish --provenance) - [ ] Verify package attestations for dependencies using npm audit signatures in CI/CD @@ -103,9 +104,9 @@ Layered defenses reduce single points of failure by using multiple controls that This guidance assumes: -- **Package ecosystem**: You're using npm or Yarn for JavaScript/TypeScript projects. Similar principles apply to other ecosystems (pip, Maven, NuGet), but specific configurations differ. +- **Package ecosystem**: This guidance uses npm or Yarn for JavaScript/TypeScript projects as examples. Similar principles apply to other ecosystems (pip, Maven, NuGet), but specific configurations differ. - **GitHub repository**: You have administrative access to configure repository settings, rulesets, and GitHub Actions workflows. -- **GitHub Advanced Security**: For organizations using GitHub Enterprise Cloud, GitHub Advanced Security features (Dependabot, dependency review, code scanning) are available. Some features are also available on public repositories. +- **Dependabot and GitHub Advanced Security features**: This guidance uses Dependabot, dependency review, code scanning, and secret protection. Feature availability varies by GitHub plan. - **Development environment**: You can configure your local development environment or are using GitHub Codespaces/dev containers. - **CI/CD with GitHub Actions**: Your build and deployment pipelines use GitHub Actions. Adapt the workflow examples if using other CI/CD systems. @@ -130,6 +131,10 @@ save-exact=true The `ignore-scripts=true` setting tells npm to skip all lifecycle scripts during installation. The `save-exact=true` setting ensures npm saves exact versions (not version ranges) in your `package.json`, preventing unexpected updates to newer versions that might contain malicious code. +{{< callout type="info" >}} +**Project root vs. home directory**: Placing `.npmrc` in the project root (committed to the repository) ensures all contributors and CI systems use the same secure configuration. This provides consistent protection across the team without relying on individual developer setups. +{{< /callout >}} + The `.npmrc` approach is recommended because it applies automatically, so you can't forget to add the flag. For one-off commands or environments where you can't modify the config, pass `--ignore-scripts` directly: ```bash @@ -199,9 +204,9 @@ Some legitimate packages require lifecycle scripts (such as `node-gyp` for nativ ### Layer 2: Use dev containers for isolation -Even with package scripts disabled, dev containers provide an additional security boundary. You can run dev containers locally with Docker, or use managed environments like [GitHub Codespaces](https://docs.github.com/enterprise-cloud@latest/codespaces) or [Microsoft Dev Box](https://azure.microsoft.com/products/dev-box) that provide container isolation without local setup. +Even with package scripts disabled, [dev containers](https://containers.dev) provide an additional security boundary. You can run dev containers locally with Docker, or use [GitHub Codespaces](https://docs.github.com/enterprise-cloud@latest/codespaces) for a fully managed dev container environment. Cloud PCs like [Windows 365](https://www.microsoft.com/en-us/windows-365) can also provide isolation from your primary machine, though they require manual Docker and dev container setup. -By isolating development in a container, malicious scripts can only access the container's ephemeral home directory, not your actual files, credentials, or SSH keys. This is exactly the kind of damage the Shai-Hulud attack attempted, destroying home directories when secrets couldn't be found. +By isolating development in a separate environment, malicious scripts can only access the environment's ephemeral home directory, not your actual files, credentials, or SSH keys. This is exactly the kind of damage the Shai-Hulud attack attempted, destroying home directories when secrets couldn't be found. #### Dev container security benefits @@ -229,7 +234,7 @@ One subtle risk in supply chain attacks is that malicious code might commit chan #### Configure commit signing -Configure commit signing using a GPG, SSH, or S/MIME key. For maximum protection against automated attacks, use a signing method that requires user interaction. Examples include using a passphrase-protected key, biometric authentication, or a hardware security key. +[Configure commit signing using a GPG, SSH, or S/MIME key](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits). For maximum protection against automated attacks, use a signing method that requires user interaction. Examples include using a passphrase-protected key, biometric authentication, or a hardware security key. {{< callout type="info" >}} **Why user interaction matters:** The key defense here isn't just the cryptographic signature - it's the human verification step. A malicious script running on your machine can access your signing key, but it can't press your fingerprint to the sensor or type your passphrase. This human-in-the-loop requirement is what blocks automated attacks from creating commits on your behalf. @@ -241,15 +246,15 @@ GitHub provides [additional documentation on configuring commit signing](https:/ ### Layer 4: Enforce repository rulesets -This defense layer happens at the repository level. Rulesets ensure that no code reaches your main branch without going through review and automated security checks. +This defense layer happens at the repository level. Rulesets ensure that no code reaches your default branch without going through review and automated security checks. #### Configure rulesets -Create a ruleset for your main branch: +Create a ruleset for your default branch: 1. Navigate to **Settings** → **Rules** → **Rulesets** 2. Add a new ruleset targeting your default branch -3. Examples of protections to configure: +3. Examples of [protection rules](https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets) to configure: - Require pull requests before merging - Require signed commits - Require status checks to pass (including security scans) @@ -265,14 +270,14 @@ Build trust across the supply chain by establishing cryptographic provenance for #### For package publishers: Configure trusted publishing -[Trusted publishing](https://docs.npmjs.com/trusted-publishers) removes the need to manage long-lived API tokens in your build systems. Instead, your CI/CD pipeline uses OIDC to authenticate directly with package registries. This eliminates the risk of stolen tokens being used to publish malicious versions. +[Trusted publishing](https://docs.npmjs.com/trusted-publishers) removes the need to manage long-lived API tokens in your build systems. Instead, your CI/CD pipeline attests the build artifacts and uses OIDC to authenticate directly with package registries. This achieves [SLSA Build Level 3](https://slsa.dev/spec/v1.0/levels#build-l3) security and enables code-to-cloud traceability, and eliminates the risk of stolen tokens being used to publish malicious versions. For packages you maintain: 1. Link your GitHub repository as a trusted publisher in your package registry settings (npm, PyPI, RubyGems, etc.) -2. Update your release workflow to use OIDC authentication instead of long-lived tokens -3. Publish with provenance attestations (e.g., `npm publish --provenance`) -4. This creates cryptographic proof that the package came from your specific repository at a specific commit +2. Update your release workflow to use [OIDC authentication](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments) instead of long-lived tokens +3. Publish with provenance attestations (e.g., `npm publish --provenance`) to create cryptographic proof on the specific commit of the source repository +4. Create [linked artifact storage records](https://docs.github.com/enterprise-cloud@latest/code-security/concepts/supply-chain-security/linked-artifacts) with the [`actions/attest`](https://github.com/actions/attest) action #### For package consumers: Validate package attestations @@ -280,7 +285,7 @@ For packages you maintain: For packages you depend on: -1. Use `npm audit signatures` to verify packages were built through GitHub Actions and identify the source repository and commit +1. Use `npm audit signatures` to verify packages have valid attestations and signatures, identifying the source repository and commit 2. Integrate attestation validation into your CI/CD pipeline for continuous verification 3. Prioritize dependencies published with attestations in your dependency selection decisions @@ -298,9 +303,9 @@ Build a comprehensive automated detection system that catches vulnerabilities at **Dependency vulnerabilities:** -Enable [Dependabot](https://docs.github.com/enterprise-cloud@latest/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) to automatically detect vulnerabilities and create pull requests for updates. Consider grouping patch updates for expedited review, assigning security team reviewers, and scheduling daily scans. Use [auto-triage rules](https://docs.github.com/enterprise-cloud@latest/code-security/dependabot/dependabot-auto-triage-rules/about-dependabot-auto-triage-rules) to reduce alert fatigue by automatically dismissing low-risk alerts or alerts for dependencies that don't affect your usage. For comprehensive guidance on managing security alerts at scale, see [Prioritizing security alert remediation](../prioritizing-alerts/). +Enable [Dependabot security updates](https://docs.github.com/enterprise-cloud@latest/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates) to automatically detect vulnerabilities and create pull requests for updates. Consider grouping patch updates for expedited review, assigning security team reviewers, and scheduling daily scans. Use [auto-triage rules](https://docs.github.com/enterprise-cloud@latest/code-security/dependabot/dependabot-auto-triage-rules/about-dependabot-auto-triage-rules) to reduce alert fatigue by automatically dismissing low-risk alerts or alerts for dependencies that don't affect your usage. For comprehensive guidance on managing security alerts at scale, see [Prioritizing security alert remediation](../prioritizing-alerts/). -Add the [dependency review action](https://github.com/actions/dependency-review-action) to your pull request workflows and require it as a status check. Configure it to fail on high-severity vulnerabilities, block problematic licenses, and warn on low [OpenSSF Scorecard](https://securityscorecards.dev/) scores. +Add the [dependency review action](https://github.com/actions/dependency-review-action) to your pull request workflows and require it as a status check to prevent potential vulnerabilities from being introduced. Configure it to fail on high-severity vulnerabilities, block problematic licenses, and warn on low [OpenSSF Scorecard](https://securityscorecards.dev/) scores. **Code vulnerabilities and secrets:** @@ -308,6 +313,10 @@ Enable [code scanning](https://docs.github.com/enterprise-cloud@latest/code-secu Enable [secret scanning](https://docs.github.com/enterprise-cloud@latest/code-security/secret-scanning/introduction/about-secret-scanning) to detect accidentally committed credentials. Configure push protection to prevent secrets from being pushed in the first place, and establish a response runbook for when alerts are triggered. +**Prioritize alerts with production context:** + +If you track your builds as [linked artifacts](https://docs.github.com/enterprise-cloud@latest/code-security/concepts/supply-chain-security/linked-artifacts) with deployment records, you can filter Dependabot and code scanning alerts based on what's actually deployed in production. Use filters like `has:deployment` and `runtime-risk` alongside EPSS and CVSS scores to [focus remediation on vulnerabilities that pose real risk](https://docs.github.com/enterprise-cloud@latest/code-security/tutorials/secure-your-organization/prioritize-alerts-in-production-code) to your running systems. + #### Review dependency updates systematically Beyond automated tooling, establish a human review process for evaluating dependency changes: @@ -370,6 +379,7 @@ Specifically, you may find the following links helpful: - [About rulesets](https://docs.github.com/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) - [npm trusted publishers](https://docs.npmjs.com/trusted-publishers) - [Verifying npm package provenance](https://docs.npmjs.com/viewing-package-provenance) +- [Using artifact attestations](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) - [About supply chain security](https://docs.github.com/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/about-supply-chain-security) - [Our plan for a more secure npm supply chain](https://github.blog/security/supply-chain-security/our-plan-for-a-more-secure-npm-supply-chain/) - GitHub's response to the Shai-Hulud attack - [The second half of software supply chain security on GitHub](https://github.blog/security/supply-chain-security/the-second-half-of-software-supply-chain-security-on-github/) - Build provenance and artifact attestations