supply-chain-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSupply Chain Security
软件供应链安全
Comprehensive guidance for securing the software supply chain, including dependency management, SBOM generation, vulnerability scanning, and protection against supply chain attacks.
本指南全面介绍软件供应链安全防护方法,包括依赖项管理、SBOM生成、漏洞扫描以及供应链攻击防护等内容。
When to Use This Skill
何时使用本技能
- Generating Software Bill of Materials (SBOM)
- Implementing SLSA framework compliance
- Setting up dependency vulnerability scanning
- Protecting against dependency confusion attacks
- Configuring lock files and integrity verification
- Implementing code signing with Sigstore
- Verifying software provenance
- Evaluating project security with OpenSSF Scorecard
- 生成软件物料清单(SBOM)
- 实现SLSA框架合规
- 搭建依赖项漏洞扫描机制
- 防护依赖混淆攻击
- 配置锁定文件与完整性验证
- 使用Sigstore实现代码签名
- 验证软件来源
- 通过OpenSSF Scorecard评估项目安全性
Quick Reference
快速参考
Supply Chain Attack Types
供应链攻击类型
| Attack Type | Description | Prevention |
|---|---|---|
| Dependency Confusion | Attacker publishes malicious package with internal package name | Namespace scoping, private registries |
| Typosquatting | Malicious packages with similar names ( | Lockfiles, careful review, tools |
| Compromised Maintainer | Legitimate package hijacked | Pin versions, verify signatures |
| Build System Attack | CI/CD pipeline compromised | SLSA compliance, hermetic builds |
| Malicious Dependency | New dependency contains malware | SCA scanning, SBOM review |
| 攻击类型 | 描述 | 防护措施 |
|---|---|---|
| 依赖混淆 | 攻击者发布与内部包名相同的恶意包 | 命名空间限定、私有注册表 |
| 包名仿冒 | 恶意包使用相似名称(如 | 锁定文件、仔细审核、工具检测 |
| 维护者账户被攻陷 | 合法包的维护者账户被劫持 | 固定版本、验证签名 |
| 构建系统攻击 | CI/CD流水线被攻陷 | 遵循SLSA合规、封闭构建 |
| 恶意依赖项 | 新增依赖项包含恶意代码 | SCA扫描、SBOM审核 |
SLSA Levels Quick Reference
SLSA等级快速参考
| Level | Requirements | Protection |
|---|---|---|
| SLSA 1 | Documentation of build process | Basic transparency |
| SLSA 2 | Authenticated provenance, hosted build | Tampering after build |
| SLSA 3 | Hardened build platform, non-falsifiable provenance | Tampering during build |
| SLSA 4 | Two-person review, hermetic builds | Insider threats |
| 等级 | 要求 | 防护能力 |
|---|---|---|
| SLSA 1 | 记录构建流程 | 基础透明度 |
| SLSA 2 | 可认证的来源、托管式构建 | 防止构建后篡改 |
| SLSA 3 | 加固的构建平台、不可伪造的来源 | 防止构建过程中篡改 |
| SLSA 4 | 双人审核、封闭构建 | 防范内部威胁 |
Essential Tools by Ecosystem
各生态系统必备工具
| Ecosystem | Vulnerability Scanning | Lock File | SBOM Generation |
|---|---|---|---|
| npm/Node.js | | | |
| Python | | | |
| Go | | | |
| .NET | | | |
| Java/Maven | OWASP Dependency-Check | | |
| Rust | | | |
| 生态系统 | 漏洞扫描 | 锁定文件 | SBOM生成 |
|---|---|---|---|
| npm/Node.js | | | |
| Python | | | |
| Go | | | |
| .NET | | | |
| Java/Maven | OWASP Dependency-Check | | |
| Rust | | | |
SBOM (Software Bill of Materials)
SBOM(软件物料清单)
SBOM Formats
SBOM格式
| Format | Standard | Best For |
|---|---|---|
| CycloneDX | OASIS | Security-focused, VEX support |
| SPDX | Linux Foundation | License compliance, legal |
| SWID | ISO/IEC 19770-2 | Software asset management |
| 格式 | 标准 | 适用场景 |
|---|---|---|
| CycloneDX | OASIS | 安全聚焦、支持VEX |
| SPDX | Linux基金会 | 许可证合规、法务场景 |
| SWID | ISO/IEC 19770-2 | 软件资产管理 |
CycloneDX SBOM Generation
CycloneDX SBOM生成
Node.js:
bash
undefinedNode.js:
bash
undefinedInstall CycloneDX CLI
Install CycloneDX CLI
npm install -g @cyclonedx/cyclonedx-npm
npm install -g @cyclonedx/cyclonedx-npm
Generate SBOM
Generate SBOM
cyclonedx-npm --output-file sbom.json
cyclonedx-npm --output-file sbom.xml --output-format xml
**Python:**
```bashcyclonedx-npm --output-file sbom.json
cyclonedx-npm --output-file sbom.xml --output-format xml
**Python:**
```bashInstall CycloneDX
Install CycloneDX
pip install cyclonedx-bom
pip install cyclonedx-bom
Generate from requirements.txt
Generate from requirements.txt
cyclonedx-py requirements -i requirements.txt -o sbom.json --format json
cyclonedx-py requirements -i requirements.txt -o sbom.json --format json
Generate from Poetry
Generate from Poetry
cyclonedx-py poetry -o sbom.json --format json
cyclonedx-py poetry -o sbom.json --format json
Generate from pip environment
Generate from pip environment
cyclonedx-py environment -o sbom.json
**.NET:**
```bashcyclonedx-py environment -o sbom.json
**.NET:**
```bashInstall CycloneDX tool
Install CycloneDX tool
dotnet tool install --global CycloneDX
dotnet tool install --global CycloneDX
Generate SBOM
Generate SBOM
dotnet CycloneDX myproject.csproj -o sbom.json -j
**Go:**
```bashdotnet CycloneDX myproject.csproj -o sbom.json -j
**Go:**
```bashInstall cyclonedx-gomod
Install cyclonedx-gomod
go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest
Generate SBOM
Generate SBOM
cyclonedx-gomod mod -json -output sbom.json
undefinedcyclonedx-gomod mod -json -output sbom.json
undefinedSBOM in CI/CD
CI/CD中的SBOM集成
yaml
undefinedyaml
undefinedGitHub Actions - Generate and upload SBOM
GitHub Actions - Generate and upload SBOM
name: Generate SBOM
on:
release:
types: [published]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Generate SBOM
uses: CycloneDX/gh-node-module-generatebom@v1
with:
output: sbom.json
- name: Upload SBOM to release
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: sbom.json
asset_name: sbom.json
asset_content_type: application/json
- name: Submit to Dependency Track
run: |
curl -X POST \
-H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
-H "Content-Type: multipart/form-data" \
-F "project=${{ github.repository }}" \
-F "bom=@sbom.json" \
"${{ secrets.DTRACK_URL }}/api/v1/bom"undefinedname: Generate SBOM
on:
release:
types: [published]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Generate SBOM
uses: CycloneDX/gh-node-module-generatebom@v1
with:
output: sbom.json
- name: Upload SBOM to release
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: sbom.json
asset_name: sbom.json
asset_content_type: application/json
- name: Submit to Dependency Track
run: |
curl -X POST \
-H "X-Api-Key: ${{ secrets.DTRACK_API_KEY }}" \
-H "Content-Type: multipart/form-data" \
-F "project=${{ github.repository }}" \
-F "bom=@sbom.json" \
"${{ secrets.DTRACK_URL }}/api/v1/bom"undefinedVulnerability Scanning
漏洞扫描
npm/Node.js
npm/Node.js
bash
undefinedbash
undefinedBuilt-in audit
Built-in audit
npm audit
npm audit --json > audit-results.json
npm audit fix # Auto-fix where possible
npm audit
npm audit --json > audit-results.json
npm audit fix # Auto-fix where possible
Check for outdated packages
Check for outdated packages
npm outdated
npm outdated
Use better-npm-audit for CI
Use better-npm-audit for CI
npx better-npm-audit audit --level moderate
undefinednpx better-npm-audit audit --level moderate
undefinedPython
Python
bash
undefinedbash
undefinedpip-audit (recommended)
pip-audit (recommended)
pip install pip-audit
pip-audit
pip-audit --fix # Auto-fix
pip-audit -r requirements.txt
pip-audit --format json > audit.json
pip install pip-audit
pip-audit
pip-audit --fix # Auto-fix
pip-audit -r requirements.txt
pip-audit --format json > audit.json
Safety (alternative)
Safety (alternative)
pip install safety
safety check
safety check -r requirements.txt
undefinedpip install safety
safety check
safety check -r requirements.txt
undefined.NET
.NET
bash
undefinedbash
undefinedBuilt-in vulnerability check
Built-in vulnerability check
dotnet list package --vulnerable
dotnet list package --vulnerable --include-transitive
dotnet list package --vulnerable
dotnet list package --vulnerable --include-transitive
Output as JSON for CI
Output as JSON for CI
dotnet list package --vulnerable --format json > vulnerabilities.json
undefineddotnet list package --vulnerable --format json > vulnerabilities.json
undefinedGo
Go
bash
undefinedbash
undefinedgovulncheck (official Go tool)
govulncheck (official Go tool)
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
govulncheck -json ./... > vuln.json
undefinedgo install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
govulncheck -json ./... > vuln.json
undefinedRust
Rust
bash
undefinedbash
undefinedcargo-audit
cargo-audit
cargo install cargo-audit
cargo audit
cargo audit --json > audit.json
cargo audit fix # Auto-fix (with cargo-audit-fix)
undefinedcargo install cargo-audit
cargo audit
cargo audit --json > audit.json
cargo audit fix # Auto-fix (with cargo-audit-fix)
undefinedLock Files and Integrity
锁定文件与完整性验证
Lock File Best Practices
锁定文件最佳实践
csharp
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Lock file verification utilities for supply chain security.
/// </summary>
public static class LockFileVerification
{
/// <summary>
/// Verify npm package-lock.json integrity hashes.
/// </summary>
public static Dictionary<string, PackageIntegrityResult> VerifyNpmIntegrity(string packageLockPath)
{
var json = File.ReadAllText(packageLockPath);
var lockData = JsonSerializer.Deserialize<NpmPackageLock>(json)!;
var results = new Dictionary<string, PackageIntegrityResult>();
foreach (var (name, info) in lockData.Packages ?? new())
{
if (string.IsNullOrEmpty(name)) continue; // Root package
if (!string.IsNullOrEmpty(info.Integrity))
{
var parts = info.Integrity.Split('-', 2);
results[name] = new PackageIntegrityResult(
HasIntegrity: true,
Algorithm: parts[0]);
}
else
{
results[name] = new PackageIntegrityResult(HasIntegrity: false, Algorithm: null);
}
}
return results;
}
/// <summary>
/// Verify NuGet packages.lock.json integrity.
/// </summary>
public static Dictionary<string, PackageIntegrityResult> VerifyNuGetLockFile(string lockFilePath)
{
var json = File.ReadAllText(lockFilePath);
var lockData = JsonSerializer.Deserialize<NuGetPackagesLock>(json)!;
var results = new Dictionary<string, PackageIntegrityResult>();
foreach (var (framework, dependencies) in lockData.Dependencies ?? new())
{
foreach (var (packageName, info) in dependencies)
{
var key = $"{packageName}@{info.Resolved}";
results[key] = new PackageIntegrityResult(
HasIntegrity: !string.IsNullOrEmpty(info.ContentHash),
Algorithm: !string.IsNullOrEmpty(info.ContentHash) ? "SHA512" : null);
}
}
return results;
}
}
public sealed record PackageIntegrityResult(bool HasIntegrity, string? Algorithm);
public sealed record NpmPackageLock(
[property: JsonPropertyName("packages")] Dictionary<string, NpmPackageInfo>? Packages);
public sealed record NpmPackageInfo(
[property: JsonPropertyName("integrity")] string? Integrity);
public sealed record NuGetPackagesLock(
[property: JsonPropertyName("dependencies")] Dictionary<string, Dictionary<string, NuGetDependencyInfo>>? Dependencies);
public sealed record NuGetDependencyInfo(
[property: JsonPropertyName("resolved")] string? Resolved,
[property: JsonPropertyName("contentHash")] string? ContentHash);csharp
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Lock file verification utilities for supply chain security.
/// </summary>
public static class LockFileVerification
{
/// <summary>
/// Verify npm package-lock.json integrity hashes.
/// </summary>
public static Dictionary<string, PackageIntegrityResult> VerifyNpmIntegrity(string packageLockPath)
{
var json = File.ReadAllText(packageLockPath);
var lockData = JsonSerializer.Deserialize<NpmPackageLock>(json)!;
var results = new Dictionary<string, PackageIntegrityResult>();
foreach (var (name, info) in lockData.Packages ?? new())
{
if (string.IsNullOrEmpty(name)) continue; // Root package
if (!string.IsNullOrEmpty(info.Integrity))
{
var parts = info.Integrity.Split('-', 2);
results[name] = new PackageIntegrityResult(
HasIntegrity: true,
Algorithm: parts[0]);
}
else
{
results[name] = new PackageIntegrityResult(HasIntegrity: false, Algorithm: null);
}
}
return results;
}
/// <summary>
/// Verify NuGet packages.lock.json integrity.
/// </summary>
public static Dictionary<string, PackageIntegrityResult> VerifyNuGetLockFile(string lockFilePath)
{
var json = File.ReadAllText(lockFilePath);
var lockData = JsonSerializer.Deserialize<NuGetPackagesLock>(json)!;
var results = new Dictionary<string, PackageIntegrityResult>();
foreach (var (framework, dependencies) in lockData.Dependencies ?? new())
{
foreach (var (packageName, info) in dependencies)
{
var key = $"{packageName}@{info.Resolved}";
results[key] = new PackageIntegrityResult(
HasIntegrity: !string.IsNullOrEmpty(info.ContentHash),
Algorithm: !string.IsNullOrEmpty(info.ContentHash) ? "SHA512" : null);
}
}
return results;
}
}
public sealed record PackageIntegrityResult(bool HasIntegrity, string? Algorithm);
public sealed record NpmPackageLock(
[property: JsonPropertyName("packages")] Dictionary<string, NpmPackageInfo>? Packages);
public sealed record NpmPackageInfo(
[property: JsonPropertyName("integrity")] string? Integrity);
public sealed record NuGetPackagesLock(
[property: JsonPropertyName("dependencies")] Dictionary<string, Dictionary<string, NuGetDependencyInfo>>? Dependencies);
public sealed record NuGetDependencyInfo(
[property: JsonPropertyName("resolved")] string? Resolved,
[property: JsonPropertyName("contentHash")] string? ContentHash);pip with Hash Verification
带哈希验证的pip配置
text
undefinedtext
undefinedrequirements.txt with hashes (most secure)
requirements.txt with hashes (most secure)
requests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
certifi==2024.2.2
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
undefinedrequests==2.31.0
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
certifi==2024.2.2
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
undefinedGenerate Hashes Automatically
自动生成哈希
bash
undefinedbash
undefinedpip-tools for hash generation
pip-tools for hash generation
pip install pip-tools
pip install pip-tools
Generate requirements with hashes
Generate requirements with hashes
pip-compile --generate-hashes requirements.in -o requirements.txt
pip-compile --generate-hashes requirements.in -o requirements.txt
Poetry with hash export
Poetry with hash export
poetry export --format requirements.txt --with-hashes > requirements.txt
undefinedpoetry export --format requirements.txt --with-hashes > requirements.txt
undefinedDependency Confusion Prevention
依赖混淆防护
Private Registry Configuration
私有注册表配置
npm (.npmrc):
ini
undefinednpm (.npmrc):
ini
undefinedScope packages to private registry
Scope packages to private registry
@mycompany:registry=https://npm.mycompany.com/
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
@mycompany:registry=https://npm.mycompany.com/
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
Always use exact versions
Always use exact versions
save-exact=true
**Python (pip.conf):**
```ini
[global]
index-url = https://pypi.mycompany.com/simple/
extra-index-url = https://pypi.org/simple/
trusted-host = pypi.mycompany.com
[install]save-exact=true
**Python (pip.conf):**
```ini
[global]
index-url = https://pypi.mycompany.com/simple/
extra-index-url = https://pypi.org/simple/
trusted-host = pypi.mycompany.com
[install]Prefer private packages
Prefer private packages
prefer-binary = true
**Preventive Measures:**
```csharp
using System.Net.Http.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Dependency confusion detection and prevention utilities.
/// </summary>
public sealed class DependencyConfusionChecker(HttpClient httpClient)
{
/// <summary>
/// Check if internal NuGet package names exist on nuget.org.
/// </summary>
public async Task<Dictionary<string, ConfusionCheckResult>> CheckNuGetConfusionAsync(
IEnumerable<string> internalPackages,
CancellationToken cancellationToken = default)
{
var results = new Dictionary<string, ConfusionCheckResult>();
foreach (var package in internalPackages)
{
try
{
var response = await httpClient.GetAsync(
$"https://api.nuget.org/v3/registration5-semver1/{package.ToLowerInvariant()}/index.json",
cancellationToken);
if (response.IsSuccessStatusCode)
{
var registration = await response.Content.ReadFromJsonAsync<NuGetRegistration>(
cancellationToken: cancellationToken);
var latestVersion = registration?.Items?.LastOrDefault()?.Upper;
results[package] = new ConfusionCheckResult(
ExistsPublicly: true,
PublicVersion: latestVersion,
Risk: ConfusionRisk.High,
Recommendation: "Register placeholder on nuget.org or use package prefix reservation");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
results[package] = new ConfusionCheckResult(
ExistsPublicly: false,
PublicVersion: null,
Risk: ConfusionRisk.Low,
Recommendation: "Consider registering placeholder package");
}
}
catch (Exception ex)
{
results[package] = new ConfusionCheckResult(
ExistsPublicly: false,
PublicVersion: null,
Risk: ConfusionRisk.Unknown,
Recommendation: $"Check failed: {ex.Message}");
}
}
return results;
}
/// <summary>
/// Generate placeholder .csproj for NuGet package reservation.
/// </summary>
public static string GeneratePlaceholderProject(
string packageId,
string description = "Internal package - not for public use")
{
return $"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>{packageId}</PackageId>
<Version>0.0.1</Version>
<Description>{description}</Description>
<Authors>Security Team</Authors>
<PackageTags>placeholder;internal;reserved</PackageTags>
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
</PropertyGroup>
</Project>
""";
}
}
public sealed record ConfusionCheckResult(
bool ExistsPublicly,
string? PublicVersion,
ConfusionRisk Risk,
string Recommendation);
public enum ConfusionRisk { Low, Medium, High, Unknown }
public sealed record NuGetRegistration(
[property: JsonPropertyName("items")] List<NuGetCatalogPage>? Items);
public sealed record NuGetCatalogPage(
[property: JsonPropertyName("upper")] string? Upper);prefer-binary = true
undefinedCode Signing with Sigstore
防护措施代码示例
Sigstore Overview
—
Sigstore provides keyless signing using OIDC identity:
bash
undefinedcsharp
using System.Net.Http.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Dependency confusion detection and prevention utilities.
/// </summary>
public sealed class DependencyConfusionChecker(HttpClient httpClient)
{
/// <summary>
/// Check if internal NuGet package names exist on nuget.org.
/// </summary>
public async Task<Dictionary<string, ConfusionCheckResult>> CheckNuGetConfusionAsync(
IEnumerable<string> internalPackages,
CancellationToken cancellationToken = default)
{
var results = new Dictionary<string, ConfusionCheckResult>();
foreach (var package in internalPackages)
{
try
{
var response = await httpClient.GetAsync(
$"https://api.nuget.org/v3/registration5-semver1/{package.ToLowerInvariant()}/index.json",
cancellationToken);
if (response.IsSuccessStatusCode)
{
var registration = await response.Content.ReadFromJsonAsync<NuGetRegistration>(
cancellationToken: cancellationToken);
var latestVersion = registration?.Items?.LastOrDefault()?.Upper;
results[package] = new ConfusionCheckResult(
ExistsPublicly: true,
PublicVersion: latestVersion,
Risk: ConfusionRisk.High,
Recommendation: "Register placeholder on nuget.org or use package prefix reservation");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
results[package] = new ConfusionCheckResult(
ExistsPublicly: false,
PublicVersion: null,
Risk: ConfusionRisk.Low,
Recommendation: "Consider registering placeholder package");
}
}
catch (Exception ex)
{
results[package] = new ConfusionCheckResult(
ExistsPublicly: false,
PublicVersion: null,
Risk: ConfusionRisk.Unknown,
Recommendation: $"Check failed: {ex.Message}");
}
}
return results;
}
/// <summary>
/// Generate placeholder .csproj for NuGet package reservation.
/// </summary>
public static string GeneratePlaceholderProject(
string packageId,
string description = "Internal package - not for public use")
{
return $"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>{packageId}</PackageId>
<Version>0.0.1</Version>
<Description>{description}</Description>
<Authors>Security Team</Authors>
<PackageTags>placeholder;internal;reserved</PackageTags>
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
</PropertyGroup>
</Project>
""";
}
}
public sealed record ConfusionCheckResult(
bool ExistsPublicly,
string? PublicVersion,
ConfusionRisk Risk,
string Recommendation);
public enum ConfusionRisk { Low, Medium, High, Unknown }
public sealed record NuGetRegistration(
[property: JsonPropertyName("items")] List<NuGetCatalogPage>? Items);
public sealed record NuGetCatalogPage(
[property: JsonPropertyName("upper")] string? Upper);Install cosign
使用Sigstore进行代码签名
macOS
Sigstore概述
brew install cosign
Sigstore提供基于OIDC身份的无密钥签名功能:
bash
undefinedLinux
Install cosign
—
macOS
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
undefinedbrew install cosign
Sign Container Images
Linux
bash
undefinedcurl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
undefinedSign with keyless (OIDC)
签名容器镜像
cosign sign ghcr.io/myorg/myimage:v1.0.0
bash
undefinedSign with key
Sign with keyless (OIDC)
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/myorg/myimage:v1.0.0
cosign sign ghcr.io/myorg/myimage:v1.0.0
Verify signature
Sign with key
cosign verify ghcr.io/myorg/myimage:v1.0.0
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
undefinedcosign generate-key-pair
cosign sign --key cosign.key ghcr.io/myorg/myimage:v1.0.0
Sign Python Packages
Verify signature
bash
undefinedcosign verify ghcr.io/myorg/myimage:v1.0.0
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
--certificate-identity=ci@myorg.com
--certificate-oidc-issuer=https://github.com/login/oauth
undefinedInstall sigstore
签名Python包
pip install sigstore
bash
undefinedSign a package
Install sigstore
python -m sigstore sign dist/mypackage-1.0.0.tar.gz
pip install sigstore
Verify signature
Sign a package
python -m sigstore verify identity
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
undefinedpython -m sigstore sign dist/mypackage-1.0.0.tar.gz
Sign npm Packages
Verify signature
bash
undefinedpython -m sigstore verify identity
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
--cert-identity ci@myorg.com
--cert-oidc-issuer https://github.com/login/oauth
dist/mypackage-1.0.0.tar.gz
undefinednpm provenance (built-in since npm 9.5.0)
签名npm包
npm publish --provenance
bash
undefinedVerify provenance
npm provenance (built-in since npm 9.5.0)
npm audit signatures
undefinednpm publish --provenance
OpenSSF Scorecard
Verify provenance
Running Scorecard
—
bash
undefinednpm audit signatures
undefinedInstall scorecard
OpenSSF Scorecard
macOS
运行Scorecard
brew install scorecard
bash
undefinedRun on GitHub repo
Install scorecard
—
macOS
scorecard --repo=github.com/myorg/myproject
brew install scorecard
Run with specific checks
Run on GitHub repo
scorecard --repo=github.com/myorg/myproject
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies
scorecard --repo=github.com/myorg/myproject
Output as JSON
Run with specific checks
scorecard --repo=github.com/myorg/myproject --format=json > scorecard.json
undefinedscorecard --repo=github.com/myorg/myproject
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies
--checks=Vulnerabilities,Dependency-Update-Tool,Pinned-Dependencies
GitHub Action for Scorecard
Output as JSON
yaml
name: Scorecard Analysis
on:
push:
branches: [main]
schedule:
- cron: '0 6 * * 1' # Weekly on Monday
permissions:
security-events: write
id-token: write
contents: read
actions: read
jobs:
analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Run Scorecard
uses: ossf/scorecard-action@v2
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload to Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifscorecard --repo=github.com/myorg/myproject --format=json > scorecard.json
undefinedScorecard Checks Explained
GitHub Action集成Scorecard
| Check | What It Measures | How to Improve |
|---|---|---|
| Vulnerabilities | Known vulnerabilities in dependencies | Enable Dependabot, fix vulns |
| Dependency-Update-Tool | Automated dependency updates | Enable Dependabot/Renovate |
| Pinned-Dependencies | CI uses pinned dependencies | Pin action versions, use hashes |
| Token-Permissions | Minimal CI token permissions | Use least-privilege tokens |
| Branch-Protection | Main branch protection | Require reviews, status checks |
| Code-Review | PRs require review | Enable required reviews |
| Signed-Releases | Releases are signed | Use Sigstore/GPG signing |
| Binary-Artifacts | Repo contains binaries | Remove binaries, use releases |
yaml
name: Scorecard Analysis
on:
push:
branches: [main]
schedule:
- cron: '0 6 * * 1' # Weekly on Monday
permissions:
security-events: write
id-token: write
contents: read
actions: read
jobs:
analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Run Scorecard
uses: ossf/scorecard-action@v2
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload to Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifSecurity Checklist
Scorecard检查项说明
Pre-Release Checklist
—
- Generate SBOM for release
- Run vulnerability scan (npm audit, pip-audit, etc.)
- Verify all dependencies have lock file entries
- Check for dependency confusion risks
- Sign release artifacts with Sigstore
- Run OpenSSF Scorecard
- Verify provenance generation is enabled
| 检查项 | 检测内容 | 优化建议 |
|---|---|---|
| Vulnerabilities | 依赖项中的已知漏洞 | 启用Dependabot、修复漏洞 |
| Dependency-Update-Tool | 是否使用自动化依赖更新工具 | 启用Dependabot/Renovate |
| Pinned-Dependencies | CI是否使用固定版本的依赖项 | 固定Action版本、使用哈希 |
| Token-Permissions | CI令牌权限是否最小化 | 使用最小权限令牌 |
| Branch-Protection | 主分支防护配置 | 启用代码审核、状态检查 |
| Code-Review | PR是否要求审核 | 启用强制审核 |
| Signed-Releases | 发布物是否签名 | 使用Sigstore/GPG签名 |
| Binary-Artifacts | 仓库是否包含二进制文件 | 删除二进制文件、使用发布机制 |
Repository Security
安全检查清单
—
发布前检查清单
- Enable Dependabot or Renovate
- Configure branch protection rules
- Pin CI/CD action versions with hashes
- Use minimal token permissions
- Enable secret scanning
- Configure code owners for security files
- 为发布版本生成SBOM
- 运行漏洞扫描(npm audit、pip-audit等)
- 验证所有依赖项均有锁定文件条目
- 检查依赖混淆风险
- 使用Sigstore签名发布物
- 运行OpenSSF Scorecard
- 验证来源生成已启用
Dependency Management
仓库安全配置
- Use lock files in all projects
- Enable integrity hash verification
- Configure private registry for internal packages
- Register placeholder packages on public registries
- Review new dependencies before adding
- Monitor for typosquatting attempts
- 启用Dependabot或Renovate
- 配置分支防护规则
- 使用哈希固定CI/CD Action版本
- 使用最小权限令牌
- 启用密钥扫描
- 为安全文件配置代码所有者
References
依赖项管理
- SBOM Generation: See for advanced SBOM workflows
references/sbom-generation.md - SLSA Framework: See for implementation guidance
references/slsa-levels.md - Attack Prevention: See for detailed attack patterns
references/dependency-attacks.md
- 所有项目均使用锁定文件
- 启用完整性哈希验证
- 为内部包配置私有注册表
- 在公共注册表注册占位包
- 新增依赖项前进行审核
- 监控包名仿冒尝试
Related Skills
参考资料
- - Secure development practices
secure-coding - - CI/CD security integration
devsecops-practices - - Container image signing and scanning
container-security
Last Updated: 2025-12-26
- SBOM生成: 高级SBOM工作流请参考
references/sbom-generation.md - SLSA框架: 实施指南请参考
references/slsa-levels.md - 攻击防护: 详细攻击模式请参考
references/dependency-attacks.md
—
相关技能
—
- - 安全开发实践
secure-coding - - CI/CD安全集成
devsecops-practices - - 容器镜像签名与扫描
container-security
最后更新: 2025-12-26