帮你快速理解、总结文档立即下载

Skill 安全检测

最近更新时间:2026-08-17 17:19:07
本文档已由 AI 辅助审校
我的收藏

功能概述

Skill 安全检测是腾讯云 WAF 面向 AI Agent Skill 的安全准入检测能力,在上架、接入和运行前对第三方 Skill 包进行自动化安全扫描,识别恶意代码、Prompt 注入、凭证窃取、供应链风险和运行时异常行为。
传统 Web 漏洞扫描解决的是 Web 应用层面的漏洞问题,而 Skill 安全检测解决的是 AI Agent 生态中更特殊的一类风险:第三方 Skill 既包含自然语言说明、Prompt 指令,也包含代码、脚本、依赖、远程链接和工具调用逻辑。
攻击者可以将恶意行为隐藏在说明文档、安装脚本、依赖包、Prompt 诱导或运行时工具链中,而传统 SAST/DAST 很难完整判断声明功能实际行为是否一致。
Skill 安全检测将以下三种检测能力统一在一条工程链路中,对上传的 Skill 包进行自动化检测:
检测能力
说明
AI 语义审计
对 Skill 的自然语言说明、Prompt 指令进行语义分析,判断声明功能与实际行为是否一致。
静态规则扫描
对 Skill 包内的代码、脚本、依赖、配置文件进行静态分析,识别已知恶意模式。
动态沙箱验证
在隔离沙箱环境中运行 Skill,观察其运行时行为,识别文件操作、网络外传、命令执行等异常行为。
检测完成后,系统输出统一的风险等级、评分、风险项和报告链接,供审核、整改和留痕。

检测类型

Skill 安全检测覆盖以下主要威胁类型:
风险维度
说明
供应链风险
检测 Skill 依赖的第三方包、远程链接是否存在已知漏洞或恶意组件。
命令注入
检测 Skill 代码或 Prompt 中是否包含恶意命令执行逻辑。
数据外传
检测 Skill 运行时是否将用户数据、系统信息发送到外部地址。
Prompt 注入
检测 Skill 的说明或指令中是否包含劫持模型行为的恶意 Prompt。
运行时异常行为
检测 Skill 运行时的文件操作、网络访问等异常行为。
说明:
具体风险维度的完整清单及评级标准以控制台实际展示为准,各维度会随检测能力迭代持续更新。

适用场景

场景
客户价值
Skill 市场上架审核
在第三方 Skill 上架前识别恶意代码、Prompt 注入、凭证窃取、供应链风险和运行时异常行为。
企业内部 SkillHub 准入
对内部员工或外部供应商提交的 Skill 建立统一安全基线。
存量 Skill 批量复扫
规则、模型或沙箱能力升级后,对历史 Skill 进行版本化重扫。
OpenAPI 自动化接入
支持客户平台、CI/CD、运营后台调用上传与查询接口。
安全运营与审计
输出可追溯的扫描结论、风险项、评分和报告,便于审核、整改和留痕。

检测流程

前提条件

已通过 SDK/API 接入 方式接入大模型安全功能并获取接入凭证。

规范要求

Skill 安全检测会基于文件内容的 SHA256 Hash 复用相同 Skill 的历史检测结果,避免重复扫描。但 ZIP 文件格式会在文件头中记录修改时间、文件系统属性等元信息,即使源文件内容完全相同,不同环境下打包产出的 ZIP 二进制也可能不同,导致 Hash 不一致、无法复用结果。因此,调用上传接口前必须按以下规范打包 ZIP,确保同一份 Skill 目录始终产出一致的 ContentHash。

打包规范
配置项
要求
打包方式
使用编程语言内置的 ZIP 库进行打包,如 Go archive/zipPython zipfile
文件排序
按路径字节序排序,其效果等价于在 Shell 中执行 LC_ALL=C sort
时间戳
固定为 2000-01-01 00:00:00 UTC
压缩算法
DEFLATE,压缩级别固定为 6
文件权限
固定为 0644
Extra Field / Comment
不写入。
目录条目
不单独写入目录条目,目录结构由文件路径自身体现。
文件过滤
Skill 目录下的所有文件均需打包,不进行任何过滤或排除。

打包示例

Go 语言

以下示例展示如何使用 Go 将 Skill 目录按规范打包为 ZIP,并生成可作为 UploadSkillSecScan 接口 FileData 参数的 Base64 编码字符串。
1. /path/to/skill_dir 替换为待检测的 Skill 目录路径。
2. 运行程序,获取 Base64 编码字符串及 ContentHash。
3. 将 Base64 字符串作为 FileData 参数传入 UploadSkillSecScan 接口。
package main

import (
"archive/zip"
"bytes"
"compress/flate"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"time"
)

// 固定时间戳:2000-01-01 00:00:00 UTC
var fixedModTime = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)

func packSkillDir(skillDir string) ([]byte, error) {
// 1. 收集所有文件的相对路径
var files []string
err := filepath.Walk(skillDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil // 不写入目录条目
}
rel, err := filepath.Rel(skillDir, path)
if err != nil {
return err
}
// 统一使用 / 作为 ZIP 内路径分隔符
files = append(files, filepath.ToSlash(rel))
return nil
})
if err != nil {
return nil, err
}

// 2. 按路径字节序排序(等价于 LC_ALL=C sort)
sort.Strings(files)

// 3. 写入 ZIP(压缩级别 6,固定时间戳,固定权限 0644)
buf := new(bytes.Buffer)
zw := zip.NewWriter(buf)
// 显式注册压缩级别 6 的 deflate writer,确保跨 Go 版本输出稳定
zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(out, 6)
})

for _, rel := range files {
header := &zip.FileHeader{
Name: rel,
Method: zip.Deflate,
Modified: fixedModTime,
}
header.SetMode(0644)
// 不写入 Extra Field / Comment
header.Extra = nil
header.Comment = ""

w, err := zw.CreateHeader(header)
if err != nil {
return nil, err
}
f, err := os.Open(filepath.Join(skillDir, filepath.FromSlash(rel)))
if err != nil {
return nil, err
}
if _, err := io.Copy(w, f); err != nil {
f.Close()
return nil, err
}
f.Close()
}
if err := zw.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: skill_package_to_base64 <skill_dir>")
os.Exit(1)
}
skillDir := strings.TrimRight(os.Args[1], string(os.PathSeparator))

zipBytes, err := packSkillDir(skillDir)
if err != nil {
fmt.Fprintf(os.Stderr, "pack failed: %v\\n", err)
os.Exit(1)
}

sum := sha256.Sum256(zipBytes)
fmt.Println("ContentHash:", hex.EncodeToString(sum[:]))
fmt.Println("FileData (Base64):")
fmt.Println(base64.StdEncoding.EncodeToString(zipBytes))
}

Python 语言

以下示例展示如何使用 Python 将 Skill 目录按规范打包为 ZIP,并生成可作为 UploadSkillSecScan 接口 FileData 参数的 Base64 编码字符串。
说明:
运行环境要求 Python 3.7 或更高版本。本示例仅依赖 Python 标准库,无需安装任何第三方包。
1. 将命令行参数替换为待检测的 Skill 目录路径:
python3 skill_package_to_base64.py /path/to/skill_dir
2. 运行程序,获取 Base64 编码字符串和 ContentHash。
3. 将 Base64 字符串作为 FileData 参数传入 UploadSkillSecScan 接口。
#!/usr/bin/env python3
# skill_package_to_base64.py
# 按规范打包 Skill 目录为 ZIP,并输出 Base64 与 ContentHash(SHA256)
import base64
import hashlib
import io
import os
import sys
import zipfile

# 固定时间戳:2000-01-01 00:00:00 UTC
FIXED_DATE_TIME = (2000, 1, 1, 0, 0, 0)
# 文件权限 0644
UNIX_FILE_MODE = 0o644


def collect_files(skill_dir: str):
"""递归收集 skill_dir 下所有文件的相对路径,按字节序排序。"""
files = []
for root, _dirs, filenames in os.walk(skill_dir):
for name in filenames:
abs_path = os.path.join(root, name)
rel = os.path.relpath(abs_path, skill_dir)
# 统一使用 / 作为 ZIP 内路径分隔符
rel = rel.replace(os.sep, "/")
files.append(rel)
# 按路径字节序排序(等价于 LC_ALL=C sort)
files.sort(key=lambda s: s.encode("utf-8"))
return files


def pack_skill_dir(skill_dir: str) -> bytes:
buf = io.BytesIO()
# 压缩级别固定为 6(Python 3.7+ 支持 compresslevel)
with zipfile.ZipFile(
buf, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=6
) as zf:
for rel in collect_files(skill_dir):
abs_path = os.path.join(skill_dir, rel.replace("/", os.sep))
with open(abs_path, "rb") as f:
data = f.read()

zi = zipfile.ZipInfo(filename=rel, date_time=FIXED_DATE_TIME)
zi.compress_type = zipfile.ZIP_DEFLATED
# 文件权限 0644 写入 external_attr 高 16 位
zi.external_attr = (UNIX_FILE_MODE & 0xFFFF) << 16
# 不写入 Extra Field / Comment
zi.extra = b""
zi.comment = b""

zf.writestr(zi, data)

return buf.getvalue()


def main():
if len(sys.argv) < 2:
print("Usage: python3 skill_package_to_base64.py <skill_dir>", file=sys.stderr)
sys.exit(1)

skill_dir = sys.argv[1].rstrip(os.sep)
if not os.path.isdir(skill_dir):
print(f"Not a directory: {skill_dir}", file=sys.stderr)
sys.exit(1)

zip_bytes = pack_skill_dir(skill_dir)
content_hash = hashlib.sha256(zip_bytes).hexdigest()

print("ContentHash:", content_hash)
print("FileData (Base64):")
print(base64.b64encode(zip_bytes).decode("ascii"))


if __name__ == "__main__":
main()

其他语言

如果您使用其他编程语言,请参考下表对应的 ZIP 库与压缩级别设置方式,并严格遵守 打包规范
语言
ZIP 库
设置压缩级别的方式
Node.js
fflate
zipSync(data, { level: 6 })
Java
java.util.zip
ZipOutputStream.setLevel(6)
Rust
zip crate + flate2 crate
ZipWriter::start_file(name, FileOptions::default().compression_level(Some(6)))

操作步骤

Skill 安全检测为异步检测,整体流程如下:
1. 按照 规范要求 将 Skill 目录打包为按规范生成的 ZIP 文件。
2. 调用 UploadSkillSecScan 接口上传 ZIP 文件(需先进行 Base64 编码)。上传成功后接口立即返回该文件的 ContentHash(SHA256),后台异步执行扫描。
说明:
当前 UploadSkillSecScan 接口限制单个 Skill ZIP 文件大小上限为 10 MB,超过限制将被拒绝。建议精简 Skill 包内容,例如剔除非必要的二进制资源、模型权重等,将文件控制在 10 MB 以内。
3. 使用上一步返回的 ContentHash,调用 DescribeSkillSecScanResult 接口轮询查询检测结果。
一般情况下,单次检测可在 5 分钟以内完成,建议轮询间隔设置为 5~10 秒
对于可疑或恶意样本,因需进行更深度的分析,检测耗时通常为 5~10 分钟,最长约 90 分钟
如果检测失败,可重新触发检测。
4. 检测完成后,系统将返回:
风险等级malicious(恶意)/ suspicious(可疑)/ benign(可信)。
命中规则详情:按检测引擎分组,展示每条风险的文件位置、行为特征等具体信息。
处置建议:针对检测结果的综合修复建议。

注意事项

Skill 安全检测解决的是 AI Agent Skill 生态中的特殊风险,与传统 Web 漏洞扫描(SAST/DAST)定位不同,建议二者配合使用。
检测结果为安全准入提供依据,建议结合企业自身的安全规范制定处置策略。
检测规则、模型和沙箱能力会持续升级,建议定期对存量 Skill 进行版本化复扫。

相关文档