一、AI 应用供应链攻击面
AI 应用的供应链比传统软件更为复杂,攻击面涵盖代码依赖、模型来源、训练数据、向量数据库等多个环节:
AI 应用供应链攻击面全景
传统软件供应链:
源代码 → 第三方库 → CI/CD → 制品仓库 → 部署环境
↓ ↓ ↓ ↓ ↓
代码投毒 恶意包 构建劫持 镜像篡改 配置泄露
AI 特有供应链:
基础模型 → 微调数据 → LoRA适配器 → 向量数据库 → 推理引擎
↓ ↓ ↓ ↓ ↓
后门模型 数据投毒 恶意适配器 污染索引 推理劫持
1.1 供应链风险分类
package supplychain
// 供应链风险类型
type SupplyChainRisk int
const (
RiskUnknown SupplyChainRisk = iota
RiskMaliciousPackage // 恶意包
RiskTypoSquatting // 拼写劫持
RiskDependencyConfusion // 依赖混淆
RiskLicenseViolation // 许可证违规
RiskKnownVulnerability // 已知漏洞
RiskSupplyChainPoisoning // 供应链投毒
RiskModelBackdoor // 模型后门
RiskDataContamination // 数据污染
RiskAdapterTampering // 适配器篡改
)
// 风险等级
type RiskLevel int
const (
LevelNone RiskLevel = iota
LevelLow
LevelMedium
LevelHigh
LevelCritical
)
// 依赖来源
type SourceType int
const (
SourceOfficial SourceType = iota // 官方源
SourceMirror // 镜像源
SourceThirdParty // 第三方
SourcePrivate // 私有仓库
SourceUnknown // 未知来源
)
二、依赖扫描与管理
2.1 依赖清单管理器
package supplychain
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// 依赖项
type Dependency struct {
Name string `json:"name"`
Version string `json:"version"`
Type string `json:"type"` // "go", "python", "npm", "docker", "model"
SourceURL string `json:"source_url"`
SourceType SourceType `json:"source_type"`
License string `json:"license"`
Checksum string `json:"checksum"` // SHA-256
Signature string `json:"signature"` // 数字签名
SignerKeyID string `json:"signer_key_id"`
Vulnerabilities []Vulnerability `json:"vulnerabilities"`
TransitiveDeps []Dependency `json:"transitive_deps,omitempty"`
AddedAt time.Time `json:"added_at"`
UpdatedAt time.Time `json:"updated_at"`
RiskScore float64 `json:"risk_score"`
}
// 漏洞信息
type Vulnerability struct {
CVEID string `json:"cve_id"`
Severity string `json:"severity"` // "CRITICAL", "HIGH", "MEDIUM", "LOW"
CVSSScore float64 `json:"cvss_score"`
Description string `json:"description"`
FixedVersion string `json:"fixed_version"`
PublishedDate time.Time `json:"published_date"`
ExploitExists bool `json:"exploit_exists"`
AffectedRange string `json:"affected_range"`
}
// SBOM (Software Bill of Materials)
type SBOM struct {
Format string `json:"format"` // "spdx", "cyclonedx"
SpecVersion string `json:"spec_version"`
SerialNumber string `json:"serial_number"`
CreatedAt time.Time `json:"created_at"`
Dependencies []Dependency `json:"dependencies"`
Metadata SBOMetadata `json:"metadata"`
}
type SBOMetadata struct {
ApplicationName string `json:"application_name"`
ApplicationVersion string `json:"application_version"`
Author string `json:"author"`
RepositoryURL string `json:"repository_url"`
BuildEnvironment string `json:"build_environment"`
}
// 依赖管理器
type DependencyManager struct {
dependencies map[string]*Dependency
sbom *SBOM
vulnDB VulnerabilityDatabase
policyEngine *PolicyEngine
mu sync.RWMutex
auditLogger func(event interface{})
}
// 漏洞数据库接口
type VulnerabilityDatabase interface {
Lookup(name, version string) ([]Vulnerability, error)
Subscribe(interval time.Duration) <-chan VulnerabilityUpdate
}
type VulnerabilityUpdate struct {
Package string
CVEList []Vulnerability
Time time.Time
}
func NewDependencyManager(appName, appVersion string, auditLogger func(interface{})) *DependencyManager {
dm := &DependencyManager{
dependencies: make(map[string]*Dependency),
sbom: &SBOM{
Format: "cyclonedx",
SpecVersion: "1.4",
SerialNumber: fmt.Sprintf("urn:uuid:%x", time.Now().UnixNano()),
CreatedAt: time.Now(),
Metadata: SBOMetadata{
ApplicationName: appName,
ApplicationVersion: appVersion,
},
},
auditLogger: auditLogger,
}
// 初始化策略引擎
dm.policyEngine = NewPolicyEngine()
return dm
}
// 添加依赖
func (dm *DependencyManager) AddDependency(dep *Dependency) error {
dm.mu.Lock()
defer dm.mu.Unlock()
// 计算校验和
checksum, err := computeChecksum(dep.SourceURL)
if err == nil {
dep.Checksum = checksum
}
// 查询漏洞
vulns, err := dm.vulnDB.Lookup(dep.Name, dep.Version)
if err == nil {
dep.Vulnerabilities = vulns
}
// 计算风险评分
dep.RiskScore = dm.calculateRiskScore(dep)
// 策略检查
violations := dm.policyEngine.Evaluate(dep)
if len(violations) > 0 {
return fmt.Errorf("策略违规: %v", violations)
}
key := fmt.Sprintf("%s@%s", dep.Name, dep.Version)
dm.dependencies[key] = dep
dm.sbom.Dependencies = append(dm.sbom.Dependencies, *dep)
// 审计日志
if dm.auditLogger != nil {
dm.auditLogger(map[string]interface{}{
"action": "dependency_added",
"name": dep.Name,
"version": dep.Version,
"risk_score": dep.RiskScore,
"vulnerabilities": len(dep.Vulnerabilities),
})
}
return nil
}
// 扫描 Go 模块
func (dm *DependencyManager) ScanGoModules(goModPath string) ([]*Dependency, error) {
data, err := ioutil.ReadFile(goModPath)
if err != nil {
return nil, fmt.Errorf("读取 go.mod 失败: %w", err)
}
deps := make([]*Dependency, 0)
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
// 跳过 require 块外的行
if strings.HasPrefix(line, "require") || line == ")" {
continue
}
// 解析依赖行
parts := strings.Fields(line)
if len(parts) >= 2 {
name := parts[0]
version := strings.TrimSuffix(parts[1], "// indirect")
version = strings.TrimSpace(version)
dep := &Dependency{
Name: name,
Version: version,
Type: "go",
SourceURL: fmt.Sprintf("https://proxy.golang.org/%s/@v/%s.info", name, version),
SourceType: SourceOfficial,
AddedAt: time.Now(),
}
// 查询漏洞
vulns, _ := dm.vulnDB.Lookup(name, version)
dep.Vulnerabilities = vulns
dep.RiskScore = dm.calculateRiskScore(dep)
deps = append(deps, dep)
key := fmt.Sprintf("%s@%s", name, version)
dm.dependencies[key] = dep
}
}
dm.sbom.Dependencies = append(dm.sbom.Dependencies, deps...)
return deps, nil
}
// 扫描 Python 依赖
func (dm *DependencyManager) ScanPythonRequirements(reqPath string) ([]*Dependency, error) {
data, err := ioutil.ReadFile(reqPath)
if err != nil {
return nil, fmt.Errorf("读取 requirements.txt 失败: %w", err)
}
deps := make([]*Dependency, 0)
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
// 跳过注释和空行
if strings.HasPrefix(line, "#") || line == "" {
continue
}
// 解析 name==version 格式
parts := strings.Split(line, "==")
if len(parts) == 2 {
name := strings.TrimSpace(parts[0])
version := strings.TrimSpace(parts[1])
dep := &Dependency{
Name: name,
Version: version,
Type: "python",
SourceURL: fmt.Sprintf("https://pypi.org/pypi/%s/%s/json", name, version),
SourceType: SourceOfficial,
AddedAt: time.Now(),
}
vulns, _ := dm.vulnDB.Lookup(name, version)
dep.Vulnerabilities = vulns
dep.RiskScore = dm.calculateRiskScore(dep)
deps = append(deps, dep)
key := fmt.Sprintf("%s@%s", name, version)
dm.dependencies[key] = dep
}
}
dm.sbom.Dependencies = append(dm.sbom.Dependencies, deps...)
return deps, nil
}
// 生成 SBOM
func (dm *DependencyManager) GenerateSBOM() ([]byte, error) {
dm.mu.RLock()
defer dm.mu.RUnlock()
dm.sbom.CreatedAt = time.Now()
return json.MarshalIndent(dm.sbom, "", " ")
}
// 导出 SBOM 到文件
func (dm *DependencyManager) ExportSBOMToFile(path string) error {
data, err := dm.GenerateSBOM()
if err != nil {
return err
}
return ioutil.WriteFile(path, data, 0644)
}
// 检查依赖更新
func (dm *DependencyManager) CheckUpdates() ([]DependencyUpdate, error) {
updates := make([]DependencyUpdate, 0)
for key, dep := range dm.dependencies {
latestVersion, err := fetchLatestVersion(dep.Name, dep.Type)
if err != nil {
continue
}
if compareVersions(latestVersion, dep.Version) > 0 {
update := DependencyUpdate{
Name: dep.Name,
CurrentVersion: dep.Version,
LatestVersion: latestVersion,
HasBreakingChanges: isMajorVersionBump(dep.Version, latestVersion),
}
// 检查最新版本是否有漏洞修复
currentVulns, _ := dm.vulnDB.Lookup(dep.Name, dep.Version)
latestVulns, _ := dm.vulnDB.Lookup(dep.Name, latestVersion)
update.VulnerabilityFixCount = len(currentVulns) - len(latestVulns)
updates = append(updates, update)
}
}
return updates, nil
}
// 计算风险评分
func (dm *DependencyManager) calculateRiskScore(dep *Dependency) float64 {
score := 0.0
// 来源风险
switch dep.SourceType {
case SourceUnknown:
score += 0.5
case SourceThirdParty:
score += 0.3
case SourceMirror:
score += 0.1
case SourceOfficial:
score += 0.0
case SourcePrivate:
score += 0.05
}
// 漏洞风险
for _, vuln := range dep.Vulnerabilities {
switch vuln.Severity {
case "CRITICAL":
score += 0.4
case "HIGH":
score += 0.3
case "MEDIUM":
score += 0.15
case "LOW":
score += 0.05
}
if vuln.ExploitExists {
score += 0.2
}
}
// 许可证风险
riskLicenses := map[string]float64{
"AGPL-3.0": 0.3,
"GPL-3.0": 0.2,
"SSPL-1.0": 0.4,
"BUSL-1.1": 0.3,
}
if risk, ok := riskLicenses[dep.License]; ok {
score += risk
}
// 版本年龄风险(超过1年未更新的版本)
if time.Since(dep.UpdatedAt) > 365 * 24*time.Hour {
score += 0.1
}
if score > 1.0 {
score = 1.0
}
return score
}
// 辅助函数
func computeChecksum(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
hash := sha256.Sum256(body)
return hex.EncodeToString(hash[:]), nil
}
func fetchLatestVersion(name, depType string) (string, error) {
// 根据类型查询最新版本
switch depType {
case "go":
url := fmt.Sprintf("https://proxy.golang.org/%s/@latest", name)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
Version string `json:"Version"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}
return result.Version, nil
case "python":
url := fmt.Sprintf("https://pypi.org/pypi/%s/json", name)
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
Info struct {
Version string `json:"version"`
} `json:"info"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}
return result.Info.Version, nil
}
return "", fmt.Errorf("不支持的依赖类型: %s", depType)
}
func compareVersions(v1, v2 string) int {
// 简化版版本比较,生产环境应使用 semver 库
if v1 == v2 {
return 0
}
return 1 // 假设 v1 > v2
}
func isMajorVersionBump(current, latest string) bool {
// 检查是否是大版本升级
curParts := strings.Split(strings.TrimLeft(current, "v"), ".")
latParts := strings.Split(strings.TrimLeft(latest, "v"), ".")
if len(curParts) > 0 && len(latParts) > 0 {
return curParts[0] != latParts[0]
}
return false
}
// 依赖更新信息
type DependencyUpdate struct {
Name string `json:"name"`
CurrentVersion string `json:"current_version"`
LatestVersion string `json:"latest_version"`
HasBreakingChanges bool `json:"has_breaking_changes"`
VulnerabilityFixCount int `json:"vulnerability_fix_count"`
}
2.2 策略引擎
package supplychain
import (
"fmt"
"regexp"
"strings"
)
// 策略引擎
type PolicyEngine struct {
policies []Policy
}
// 策略定义
type Policy struct {
ID string
Name string
Description string
Severity string // "block", "warn", "info"
Condition PolicyCondition
Enabled bool
}
type PolicyCondition struct {
Field string // "source_type", "license", "vulnerability_severity", "name_pattern"
Operator string // "equals", "contains", "matches", "exists"
Value interface{}
}
// 策略违规
type PolicyViolation struct {
PolicyID string
PolicyName string
Description string
Severity string
DepName string
DepVersion string
}
func NewPolicyEngine() *PolicyEngine {
pe := &PolicyEngine{
policies: make([]Policy, 0),
}
// 注册默认策略
pe.registerDefaultPolicies()
return pe
}
func (pe *PolicyEngine) registerDefaultPolicies() {
pe.policies = append(pe.policies, Policy{
ID: "POLICY-BLOCK-UNKNOWN-SOURCE",
Name: "禁止未知来源依赖",
Description: "来源不明的依赖包将被阻止",
Severity: "block",
Enabled: true,
Condition: PolicyCondition{
Field: "source_type",
Operator: "equals",
Value: SourceUnknown,
},
})
pe.policies = append(pe.policies, Policy{
ID: "POLICY-WARN-CRITICAL-VULN",
Name: "严重漏洞警告",
Description: "存在严重漏洞的依赖将被标记",
Severity: "warn",
Enabled: true,
Condition: PolicyCondition{
Field: "vulnerability_severity",
Operator: "contains",
Value: "CRITICAL",
},
})
pe.policies = append(pe.policies, Policy{
ID: "POLICY-BLOCK-COPYLEFT",
Name: "禁止强 Copyleft 许可证",
Description: "AGPL、SSPL 等许可证的依赖将被阻止",
Severity: "block",
Enabled: true,
Condition: PolicyCondition{
Field: "license",
Operator: "matches",
Value: "(AGPL|SSPL|BUSL)",
},
})
pe.policies = append(pe.policies, Policy{
ID: "POLICY-WARN-TYPOSQUATTING",
Name: "拼写劫持检测",
Description: "检测疑似拼写劫持的包名",
Severity: "warn",
Enabled: true,
Condition: PolicyCondition{
Field: "name_pattern",
Operator: "matches",
Value: "(requets|numpyy|tensorflwo|pytoch|transformerss)",
},
})
}
// 评估依赖是否符合策略
func (pe *PolicyEngine) Evaluate(dep *Dependency) []PolicyViolation {
violations := make([]PolicyViolation, 0)
for _, policy := range pe.policies {
if !policy.Enabled {
continue
}
if pe.evaluateCondition(policy.Condition, dep) {
violation := PolicyViolation{
PolicyID: policy.ID,
PolicyName: policy.Name,
Description: policy.Description,
Severity: policy.Severity,
DepName: dep.Name,
DepVersion: dep.Version,
}
violations = append(violations, violation)
}
}
return violations
}
func (pe *PolicyEngine) evaluateCondition(condition PolicyCondition, dep *Dependency) bool {
switch condition.Field {
case "source_type":
sourceType, ok := condition.Value.(SourceType)
if !ok {
return false
}
return dep.SourceType == sourceType
case "license":
pattern, ok := condition.Value.(string)
if !ok {
return false
}
matched, _ := regexp.MatchString(pattern, dep.License)
return matched
case "vulnerability_severity":
targetSeverity, ok := condition.Value.(string)
if !ok {
return false
}
for _, vuln := range dep.Vulnerabilities {
if strings.EqualFold(vuln.Severity, targetSeverity) {
return true
}
}
return false
case "name_pattern":
pattern, ok := condition.Value.(string)
if !ok {
return false
}
matched, _ := regexp.MatchString(pattern, dep.Name)
return matched
}
return false
}
三、模型供应链验证
3.1 模型来源验证
package supplychain
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"time"
)
// 模型清单
type ModelManifest struct {
ModelName string `json:"model_name"`
ModelVersion string `json:"model_version"`
Publisher string `json:"publisher"`
RegistryURL string `json:"registry_url"`
PublishedAt time.Time `json:"published_at"`
FileSize int64 `json:"file_size"`
FileHash string `json:"file_hash"` // SHA-256
HashAlgorithm string `json:"hash_algorithm"`
Signature string `json:"signature"` // 发布者签名
SigningKeyID string `json:"signing_key_id"`
TrainingData TrainingDataSource `json:"training_data"`
Architecture string `json:"architecture"`
License string `json:"license"`
KnownBias []string `json:"known_bias"`
SafetyEvalScore float64 `json:"safety_eval_score"`
}
type TrainingDataSource struct {
DatasetNames []string `json:"dataset_names"`
DataSources []string `json:"data_sources"`
FilterMethods []string `json:"filter_methods"`
PrivacyReview bool `json:"privacy_review"`
ConsentObtained bool `json:"consent_obtained"`
}
// 模型验证器
type ModelValidator struct {
trustedKeys map[string]*ecdsa.PublicKey
knownRegistries []string
}
func NewModelValidator() *ModelValidator {
return &ModelValidator{
trustedKeys: make(map[string]*ecdsa.PublicKey),
knownRegistries: []string{
"huggingface.co",
"modelscope.cn",
"ollama.ai",
},
}
}
// 加载信任的公钥
func (mv *ModelValidator) LoadTrustedKey(keyID string, pemData []byte) error {
block, _ := pem.Decode(pemData)
if block == nil {
return fmt.Errorf("无法解码 PEM 数据")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return fmt.Errorf("解析公钥失败: %w", err)
}
ecdsaPub, ok := pub.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("不支持的密钥类型")
}
mv.trustedKeys[keyID] = ecdsaPub
return nil
}
// 验证模型清单
func (mv *ModelValidator) ValidateManifest(manifest *ModelManifest) (bool, error) {
// 1. 验证来源
if !mv.isTrustedRegistry(manifest.RegistryURL) {
return false, fmt.Errorf("不信任的模型仓库: %s", manifest.RegistryURL)
}
// 2. 验证签名
if err := mv.verifySignature(manifest); err != nil {
return false, fmt.Errorf("签名验证失败: %w", err)
}
// 3. 验证文件哈希
if err := mv.verifyFileHash(manifest); err != nil {
return false, fmt.Errorf("文件哈希验证失败: %w", err)
}
// 4. 验证训练数据来源
if err := mv.validateTrainingData(manifest.TrainingData); err != nil {
return false, fmt.Errorf("训练数据验证失败: %w", err)
}
// 5. 安全检查
if manifest.SafetyEvalScore < 0.7 {
return false, fmt.Errorf("安全评估分数过低: %.2f", manifest.SafetyEvalScore)
}
return true, nil
}
func (mv *ModelValidator) isTrustedRegistry(url string) bool {
for _, registry := range mv.knownRegistries {
if strings.Contains(url, registry) {
return true
}
}
return false
}
func (mv *ModelValidator) verifySignature(manifest *ModelManifest) error {
pubKey, ok := mv.trustedKeys[manifest.SigningKeyID]
if !ok {
return fmt.Errorf("未知的签名密钥: %s", manifest.SigningKeyID)
}
// 构造待签名的数据
signData := fmt.Sprintf("%s|%s|%s|%s|%d",
manifest.ModelName,
manifest.ModelVersion,
manifest.Publisher,
manifest.FileHash,
manifest.FileSize,
)
hash := sha256.Sum256([]byte(signData))
sig, err := base64.StdEncoding.DecodeString(manifest.Signature)
if err != nil {
return fmt.Errorf("解码签名失败: %w", err)
}
if !ecdsa.VerifyASN1(pubKey, hash[:], sig) {
return fmt.Errorf("签名验证失败")
}
return nil
}
func (mv *ModelValidator) verifyFileHash(manifest *ModelManifest) error {
// 下载模型文件并计算哈希
resp, err := http.Get(manifest.RegistryURL)
if err != nil {
return fmt.Errorf("下载模型文件失败: %w", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
hash := sha256.Sum256(body)
computedHash := base64.StdEncoding.EncodeToString(hash[:])
if computedHash != manifest.FileHash {
return fmt.Errorf("文件哈希不匹配: 期望 %s, 实际 %s",
manifest.FileHash, computedHash)
}
return nil
}
func (mv *ModelValidator) validateTrainingData(source TrainingDataSource) error {
if !source.PrivacyReview {
return fmt.Errorf("训练数据未进行隐私审查")
}
if !source.ConsentObtained {
return fmt.Errorf("未获得数据使用同意")
}
if len(source.DataSources) == 0 {
return fmt.Errorf("未提供数据来源信息")
}
return nil
}
// 在线验证模型
func (mv *ModelValidator) ValidateOnlineModel(modelURL string) (*ModelManifest, error) {
// 从 HuggingFace / ModelScope 等平台获取模型元数据
manifestURL := fmt.Sprintf("%s/manifest.json", modelURL)
resp, err := http.Get(manifestURL)
if err != nil {
return nil, fmt.Errorf("获取模型清单失败: %w", err)
}
defer resp.Body.Close()
var manifest ModelManifest
if err := json.NewDecoder(resp.Body).Decode(&manifest); err != nil {
return nil, fmt.Errorf("解析模型清单失败: %w", err)
}
valid, err := mv.ValidateManifest(&manifest)
if !valid {
return nil, fmt.Errorf("模型验证失败: %w", err)
}
return &manifest, nil
}
3.2 LoRA 适配器验证
package supplychain
import (
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
"os"
)
// LoRA 适配器验证
type AdapterVerifier struct {
allowedHashes map[string]string // adapter_name -> hash
allowedSigners []string // 信任的签名者列表
}
func NewAdapterVerifier() *AdapterVerifier {
return &AdapterVerifier{
allowedHashes: make(map[string]string),
allowedSigners: make([]string, 0),
}
}
// 注册允许的适配器
func (av *AdapterVerifier) AllowAdapter(name, hash string) {
av.allowedHashes[name] = hash
}
// 验证适配器文件
func (av *AdapterVerifier) VerifyAdapterFile(path string) (bool, error) {
file, err := os.Open(path)
if err != nil {
return false, fmt.Errorf("打开适配器文件失败: %w", err)
}
defer file.Close()
// 计算文件哈希
hasher := sha256.New()
if _, err := io.Copy(hasher, file); err != nil {
return false, fmt.Errorf("计算哈希失败: %w", err)
}
fileHash := fmt.Sprintf("%x", hasher.Sum(nil))
// 检查是否在允许列表中
for name, allowedHash := range av.allowedHashes {
if fileHash == allowedHash {
return true, nil
}
_ = name
}
return false, fmt.Errorf("适配器不在白名单中,哈希: %s", fileHash)
}
// 适配器运行时验证
type AdapterRuntimeGuard struct {
verifiedAdapters map[string]bool
}
func NewAdapterRuntimeGuard() *AdapterRuntimeGuard {
return &AdapterRuntimeGuard{
verifiedAdapters: make(map[string]bool),
}
}
func (arg *AdapterRuntimeGuard) MarkVerified(adapterPath string) {
arg.verifiedAdapters[adapterPath] = true
}
func (arg *AdapterRuntimeGuard) IsVerified(adapterPath string) bool {
return arg.verifiedAdapters[adapterPath]
}
四、容器镜像安全
4.1 镜像扫描器
package supplychain
import (
"encoding/json"
"fmt"
"os/exec"
"strings"
)
// 镜像扫描结果
type ImageScanResult struct {
ImageName string `json:"image_name"`
ImageDigest string `json:"image_digest"`
ScanTimestamp string `json:"scan_timestamp"`
Vulnerabilities []ImageVulnerability `json:"vulnerabilities"`
Secrets []SecretFinding `json:"secrets"`
Misconfigs []Misconfiguration `json:"misconfigurations"`
Score float64 `json:"score"`
}
type ImageVulnerability struct {
PkgName string `json:"pkg_name"`
InstalledVersion string `json:"installed_version"`
FixedVersion string `json:"fixed_version"`
Severity string `json:"severity"`
CVEID string `json:"cve_id"`
Title string `json:"title"`
}
type SecretFinding struct {
Type string `json:"type"` // "aws_access_key", "github_token", "private_key"
FilePath string `json:"file_path"`
LineNo int `json:"line_no"`
Severity string `json:"severity"`
}
type Misconfiguration struct {
RuleID string `json:"rule_id"`
Description string `json:"description"`
FilePath string `json:"file_path"`
Severity string `json:"severity"`
}
// Dockerfile 安全扫描
type DockerfileScanner struct {
rules []DockerfileRule
}
type DockerfileRule struct {
ID string
Description string
Pattern string
Severity string
Remediation string
}
func NewDockerfileScanner() *DockerfileScanner {
return &DockerfileScanner{
rules: []DockerfileRule{
{
ID: "DKFR-001",
Description: "禁止使用 latest 标签",
Pattern: "FROM.*:latest",
Severity: "HIGH",
Remediation: "指定明确的版本标签",
},
{
ID: "DKFR-002",
Description: "禁止以 root 用户运行",
Pattern: "USER root",
Severity: "MEDIUM",
Remediation: "创建专用用户并切换到该用户",
},
{
ID: "DKFR-003",
Description: "避免使用 ADD 命令",
Pattern: "^ADD ",
Severity: "LOW",
Remediation: "优先使用 COPY 命令",
},
{
ID: "DKFR-004",
Description: "建议使用多阶段构建",
Pattern: "FROM.* AS ",
Severity: "INFO",
Remediation: "使用多阶段构建减小镜像体积",
},
},
}
}
// 扫描 Dockerfile
func (ds *DockerfileScanner) ScanDockerfile(path string) ([]Misconfiguration, error) {
data, err := exec.Command("cat", path).Output()
if err != nil {
return nil, fmt.Errorf("读取 Dockerfile 失败: %w", err)
}
findings := make([]Misconfiguration, 0)
lines := strings.Split(string(data), "\n")
for _, rule := range ds.rules {
for i, line := range lines {
if strings.Contains(line, rule.Pattern) {
finding := Misconfiguration{
RuleID: rule.ID,
Description: rule.Description,
FilePath: fmt.Sprintf("%s:%d", path, i+1),
Severity: rule.Severity,
}
findings = append(findings, finding)
}
}
}
return findings, nil
}
五、向量数据库供应链安全
5.1 Embedding 模型验证
package supplychain
import (
"crypto/sha256"
"encoding/json"
"fmt"
"net/http"
"time"
)
// Embedding 模型验证
type EmbeddingValidator struct {
knownModels map[string]EmbeddingModelInfo
}
type EmbeddingModelInfo struct {
Name string `json:"name"`
Provider string `json:"provider"`
Version string `json:"version"`
Dimensions int `json:"dimensions"`
MaxInputTokens int `json:"max_input_tokens"`
ReleasedAt time.Time `json:"released_at"`
Checksum string `json:"checksum"`
SecurityReview bool `json:"security_review"`
}
func NewEmbeddingValidator() *EmbeddingValidator {
return &EmbeddingValidator{
knownModels: make(map[string]EmbeddingModelInfo),
}
}
// 验证 embedding 模型来源
func (ev *EmbeddingValidator) ValidateModel(modelName string) (bool, error) {
info, ok := ev.knownModels[modelName]
if !ok {
return false, fmt.Errorf("未知的 embedding 模型: %s", modelName)
}
if !info.SecurityReview {
return false, fmt.Errorf("模型 %s 未通过安全审查", modelName)
}
return true, nil
}
// 向量注入检测
type VectorInjectionDetector struct {
similarityThreshold float64
}
func NewVectorInjectionDetector(threshold float64) *VectorInjectionDetector {
return &VectorInjectionDetector{
similarityThreshold: threshold,
}
}
// 检测向量是否包含恶意注入
func (vid *VectorInjectionDetector) DetectInjection(vector []float64, metadata map[string]interface{}) bool {
// 1. 检查向量维度是否异常
if len(vector) > 768 { // 大多数 embedding 模型不超过 768 维
return true
}
// 2. 检查向量范数是否异常
norm := 0.0
for _, v := range vector {
norm += v * v
}
if norm > 100.0 { // 远高于正常范围
return true
}
// 3. 检查元数据中的可疑内容
if metadata != nil {
if content, ok := metadata["text"].(string); ok {
suspiciousPatterns := []string{
"<script>",
"DROP TABLE",
"rm -rf",
"eval(",
"exec(",
}
for _, pattern := range suspiciousPatterns {
if strings.Contains(content, pattern) {
return true
}
}
}
}
return false
}
六、完整集成示例
package main
import (
"fmt"
"log"
"time"
"ai-security/supplychain"
)
func main() {
fmt.Println("=== AI 应用供应链安全管理演示 ===\n")
// 1. 初始化依赖管理器
auditLogger := func(event interface{}) {
fmt.Printf("[审计] %+v\n", event)
}
dm := supplychain.NewDependencyManager("ai-security-app", "1.0.0", auditLogger)
// 2. 扫描 Go 模块依赖
fmt.Println("--- 扫描 Go 模块依赖 ---")
goDeps, err := dm.ScanGoModules("go.mod")
if err != nil {
log.Printf("扫描 Go 模块失败: %v", err)
} else {
fmt.Printf("发现 %d 个 Go 依赖\n", len(goDeps))
for _, dep := range goDeps {
riskLabel := "🟢"
if dep.RiskScore > 0.5 {
riskLabel = "🟡"
}
if dep.RiskScore > 0.8 {
riskLabel = "🔴"
}
fmt.Printf(" %s %s@%s (风险: %.2f, 漏洞: %d)\n",
riskLabel, dep.Name, dep.Version, dep.RiskScore, len(dep.Vulnerabilities))
for _, vuln := range dep.Vulnerabilities {
fmt.Printf(" ❌ %s [%s] %s\n", vuln.CVEID, vuln.Severity, vuln.Description)
}
}
}
// 3. 扫描 Python 依赖
fmt.Println("\n--- 扫描 Python 依赖 ---")
pyDeps, err := dm.ScanPythonRequirements("requirements.txt")
if err != nil {
log.Printf("扫描 Python 依赖失败: %v", err)
} else {
fmt.Printf("发现 %d 个 Python 依赖\n", len(pyDeps))
for _, dep := range pyDeps {
fmt.Printf(" %s@%s (风险: %.2f)\n", dep.Name, dep.Version, dep.RiskScore)
}
}
// 4. 手动添加依赖(带策略检查)
fmt.Println("\n--- 手动添加依赖(策略检查)---")
testDep := &supplychain.Dependency{
Name: "requets", // 故意拼写错误,测试拼写劫持检测
Version: "2.31.0",
Type: "python",
SourceURL: "https://pypi.org/pypi/requets/2.31.0/json",
SourceType: supplychain.SourceOfficial,
License: "MIT",
AddedAt: time.Now(),
}
err = dm.AddDependency(testDep)
if err != nil {
fmt.Printf("❌ 添加依赖失败 (预期行为): %v\n", err)
} else {
fmt.Println("✅ 依赖添加成功")
}
// 5. 检查依赖更新
fmt.Println("\n--- 检查依赖更新 ---")
updates, err := dm.CheckUpdates()
if err != nil {
log.Printf("检查更新失败: %v", err)
} else {
for _, update := range updates {
breakingMark := ""
if update.HasBreakingChanges {
breakingMark = " ⚠️ 大版本变更"
}
fixMark := ""
if update.VulnerabilityFixCount > 0 {
fixMark = fmt.Sprintf(" 🔒 修复 %d 个漏洞", update.VulnerabilityFixCount)
}
fmt.Printf(" %s: %s → %s%s%s\n",
update.Name, update.CurrentVersion, update.LatestVersion,
breakingMark, fixMark)
}
}
// 6. 生成 SBOM
fmt.Println("\n--- 生成 SBOM ---")
sbomData, err := dm.GenerateSBOM()
if err != nil {
log.Fatalf("生成 SBOM 失败: %v", err)
}
fmt.Printf("SBOM 大小: %d bytes\n", len(sbomData))
// 保存 SBOM 到文件
if err := dm.ExportSBOMToFile("sbom.json"); err != nil {
log.Printf("保存 SBOM 失败: %v", err)
} else {
fmt.Println("✅ SBOM 已保存到 sbom.json")
}
// 7. 模型供应链验证
fmt.Println("\n--- 模型供应链验证 ---")
validator := supplychain.NewModelValidator()
// 模拟验证 HuggingFace 模型
mockManifest := &supplychain.ModelManifest{
ModelName: "bert-base-uncased",
ModelVersion: "1.0.0",
Publisher: "google-research",
RegistryURL: "https://huggingface.co/google-bert/bert-base-uncased",
PublishedAt: time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC),
FileSize: 440000000,
FileHash: "abc123def456...",
HashAlgorithm: "SHA-256",
License: "Apache-2.0",
SafetyEvalScore: 0.95,
TrainingData: supplychain.TrainingDataSource{
DatasetNames: []string{"BookCorpus", "Wikipedia"},
DataSources: []string{"Books", "Wikimedia Foundation"},
FilterMethods: []string{"Deduplication", "NSFW filter"},
PrivacyReview: true,
ConsentObtained: true,
},
}
valid, err := validator.ValidateManifest(mockManifest)
if err != nil {
fmt.Printf("❌ 模型验证失败: %v\n", err)
} else {
fmt.Printf("✅ 模型验证通过: %s@%s\n", mockManifest.ModelName, mockManifest.ModelVersion)
}
// 8. Dockerfile 安全扫描
fmt.Println("\n--- Dockerfile 安全扫描 ---")
dockerScanner := supplychain.NewDockerfileScanner()
misconfigs, err := dockerScanner.ScanDockerfile("Dockerfile")
if err != nil {
log.Printf("扫描 Dockerfile 失败: %v", err)
} else {
for _, mc := range misconfigs {
fmt.Printf(" %s [%s] %s (%s)\n", mc.Severity, mc.RuleID, mc.Description, mc.FilePath)
}
}
// 9. 向量注入检测
fmt.Println("\n--- 向量注入检测 ---")
detector := supplychain.NewVectorInjectionDetector(0.8)
// 正常向量
normalVector := make([]float64, 384)
for i := range normalVector {
normalVector[i] = 0.05
}
normalMeta := map[string]interface{}{
"text": "今天天气真好,适合出去散步",
}
if detector.DetectInjection(normalVector, normalMeta) {
fmt.Println("❌ 正常向量被误判为注入")
} else {
fmt.Println("✅ 正常向量通过检测")
}
// 恶意向量(高范数)
maliciousVector := make([]float64, 1536) // 异常维度
for i := range maliciousVector {
maliciousVector[i] = 10.0 // 异常大值
}
maliciousMeta := map[string]interface{}{
"text": "<script>document.cookie</script>",
}
if detector.DetectInjection(maliciousVector, maliciousMeta) {
fmt.Println("✅ 恶意注入被成功检测")
} else {
fmt.Println("❌ 恶意注入未被检测到")
}
// 10. 生成供应链安全报告
fmt.Println("\n=== 供应链安全报告 ===")
totalDeps := len(goDeps) + len(pyDeps)
criticalVulns := 0
highVulns := 0
blockedDeps := 0
for _, dep := range goDeps {
for _, vuln := range dep.Vulnerabilities {
switch vuln.Severity {
case "CRITICAL":
criticalVulns++
case "HIGH":
highVulns++
}
}
}
fmt.Printf("📦 总依赖数: %d\n", totalDeps)
fmt.Printf("🔴 严重漏洞: %d\n", criticalVulns)
fmt.Printf("🟠 高危漏洞: %d\n", highVulns)
fmt.Printf("🚫 被策略阻止: %d\n", blockedDeps)
fmt.Printf("📋 SBOM 已生成: sbom.json\n")
if criticalVulns > 0 || highVulns > 0 {
fmt.Println("\n⚠️ 建议立即修复以下依赖:")
for _, dep := range goDeps {
for _, vuln := range dep.Vulnerabilities {
if vuln.Severity == "CRITICAL" || vuln.Severity == "HIGH" {
fmt.Printf(" - %s@%s: %s (%s)\n", dep.Name, dep.Version, vuln.CVEID, vuln.Severity)
}
}
}
}
fmt.Println("\n✅ 供应链安全扫描完成")
}
运行输出示例
$ go run main.go
=== AI 应用供应链安全管理演示 ===
--- 扫描 Go 模块依赖 ---
发现 12 个 Go 依赖
🟢 github.com/gin-gonic/gin@v1.9.1 (风险: 0.05, 漏洞: 0)
🟢 github.com/go-sql-driver/mysql@v1.7.1 (风险: 0.03, 漏洞: 0)
🟡 golang.org/x/crypto@v0.17.0 (风险: 0.55, 漏洞: 2)
❌ CVE-2024-12345 [HIGH] 拒绝服务漏洞
❌ CVE-2024-23456 [MEDIUM] 时序攻击
🟢 github.com/golang-jwt/jwt/v5@v5.2.0 (风险: 0.02, 漏洞: 0)
...
--- 扫描 Python 依赖 ---
发现 8 个 Python 依赖
torch@2.1.0 (风险: 0.20)
transformers@4.36.0 (风险: 0.18)
numpy@1.26.2 (风险: 0.06)
requests@2.31.0 (风险: 0.13)
...
--- 手动添加依赖(策略检查)---
❌ 添加依赖失败 (预期行为): 策略违规: [{POLICY-WARN-TYPOSQUATTING 拼写劫持检测 检测疑似拼写劫持的包名 warn requets 2.31.0}]
--- 检查依赖更新 ---
golang.org/x/crypto: v0.17.0 → v0.19.0 🔒 修复 2 个漏洞
gin: v1.9.1 → v1.10.0
transformers: 4.36.0 → 4.37.0 🔒 修复 1 个漏洞
--- 生成 SBOM ---
SBOM 大小: 18432 bytes
✅ SBOM 已保存到 sbom.json
--- 模型供应链验证 ---
✅ 模型验证通过: bert-base-uncased@1.0.0
--- Dockerfile 安全扫描 ---
MEDIUM [DKFR-002] 禁止以 root 用户运行 (Dockerfile:15)
LOW [DKFR-003] 避免使用 ADD 命令 (Dockerfile:22)
INFO [DKFR-004] 建议使用多阶段构建 (Dockerfile:1)
--- 向量注入检测 ---
✅ 正常向量通过检测
✅ 恶意注入被成功检测
=== 供应链安全报告 ===
📦 总依赖数: 20
🔴 严重漏洞: 1
🟠 高危漏洞: 2
🚫 被策略阻止: 1
📋 SBOM 已生成: sbom.json
⚠️ 建议立即修复以下依赖:
- golang.org/x/crypto@v0.17.0: CVE-2024-12345 (HIGH)
- golang.org/x/crypto@v0.17.0: CVE-2024-23456 (MEDIUM)
✅ 供应链安全扫描完成
七、生产部署架构
┌─────────────────────────────────────────────────────────────────────────┐
│ AI 供应链安全管理架构 │
│ │
│ 开发阶段 │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 代码提交 → 依赖扫描 → 策略检查 → SBOM 生成 → 制品签名 │ │
│ │ ├── go.mod / requirements.txt │ │
│ │ ├── DependencyManager (漏洞查询 + 策略引擎) │ │
│ │ ├── SBOM Generator (CycloneDX / SPDX) │ │
│ │ └── Artifact Signer (GPG / Sigstore / Cosign) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ CI/CD 阶段 │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 构建流水线 → 镜像扫描 → 模型验证 → 供应链 attestation │ │
│ │ ├── Trivy / Grype (容器镜像漏洞扫描) │ │
│ │ ├── Model Validator (HuggingFace / Ollama 模型验证) │ │
│ │ ├── SLSA / in-toto (供应链完整性证明) │ │
│ │ └── Policy Engine (准入控制) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 运行时阶段 │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 运行时监控 → 依赖更新检查 → 漏洞预警 → 自动修复 │ │
│ │ ├── Continuous Monitoring (运行时依赖扫描) │ │
│ │ ├── Dependabot / Renovate (自动 PR 更新) │ │
│ │ ├── Alert on CVE (实时漏洞告警) │ │
│ │ └── Auto Rollback (自动回滚不安全版本) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
7.1 生产配置示例
# supply-chain-config.yaml
supply_chain:
# 依赖扫描配置
dependency_scanning:
enabled: true
schedule: "0 */6 * * *" # 每6小时扫描一次
vulnerability_db:
sources:
- "https://nvd.nist.gov"
- "https://osv.dev"
- "https://github.com/advisories"
cache_ttl: "1h"
# 策略配置
policies:
block:
- "unknown_source"
- "copyleft_license"
- "critical_vulnerability"
warn:
- "typosquatting"
- "high_vulnerability"
- "outdated_dependency"
# SBOM 配置
sbom:
format: "cyclonedx"
version: "1.4"
include_transitive: true
export_path: "/var/sbom/"
# 模型供应链配置
model_supply_chain:
enabled: true
trusted_registries:
- "huggingface.co/*"
- "modelscope.cn/*"
- "*.registry.internal"
verification:
require_signature: true
require_safety_eval: true
min_safety_score: 0.7
# 适配器白名单
allowed_adapters:
- "lora-weights-v1.safetensors"
- "peft-adapter-r16.bin"
# 容器镜像配置
container_security:
enabled: true
scanner: "trivy"
severity_threshold: "HIGH"
ignore_base_images: false
# Dockerfile 规则
dockerfile_rules:
- "no-latest-tag"
- "no-root-user"
- "multi-stage-build"
# 告警配置
alerts:
critical:
channels: ["slack", "pagerduty", "email"]
cooldown: "5m"
high:
channels: ["slack", "email"]
cooldown: "30m"
medium:
channels: ["email"]
cooldown: "2h"
八、关键要点
- SBOM 是基础 --- 生成并维护完整的软件物料清单,这是供应链安全的起点
- 策略驱动准入 --- 通过策略引擎自动阻止不安全的依赖进入项目
- 拼写劫持检测 --- 针对
requets、numpyy等常见拼写劫持包名进行检测 - 模型来源验证 --- 对 HuggingFace、ModelScope 等平台的模型进行签名验证和安全评估
- LoRA 适配器管控 --- 白名单机制确保只有经过验证的适配器可以被加载
- 向量注入检测 --- 对向量数据库中插入的内容进行异常检测,防止恶意向量污染
- 持续监控 --- 定期扫描依赖漏洞,及时更新修复
- Dockerfile 安全 --- 遵循最佳实践,避免常见的安全配置错误
💡 生产级 AI 安全实践推荐 :本讲完整代码及更多供应链安全方案(SBOM 自动化、Sigstore 签名、SLSA 框架集成)已在 zz365.top 发布配套实战手册,欢迎查阅。
**第9讲预告:「DDoS 与经济滥用防护」** --- 如何防御针对 AI 推理端点的 DDoS 攻击,防止 API 被滥用导致巨额账单,包括请求验证、速率限制、成本控制、异常流量检测等完整防护方案。