OWASP(Open Web Application Security Project)Top 10 是全球最具影响力的 Web 应用安全风险清单,每 3-4 年更新一次。2021 版本反映了现代云原生、微服务、DevOps 时代的新威胁。本文逐项深度解析每个风险的原理与防御方案。
1. A01:2021 失效的访问控制(Broken Access Control)
1.1 风险描述
访问控制确保用户只能执行其权限范围内的操作。当访问控制失效时,攻击者可以访问未授权的功能或数据。
1.2 常见漏洞
水平越权:
GET /api/orders/12345 → 正常访问自己的订单
GET /api/orders/12346 → 未验证是否属于自己,看到他人订单!
垂直越权:
普通用户访问管理员接口:
POST /admin/delete-user → 未验证角色,普通用户也能调用!
不安全的直接对象引用(IDOR):
GET /documents?id=1001 → 通过遍历 id 获取他人文件
1.3 防御方案
@RestController
public class OrderController {
@GetMapping("/api/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId,
@AuthenticationPrincipal User user) {
Order order = orderService.findById(orderId);
// ✅ 必须验证资源归属
if (!order.getUserId().equals(user.getId())) {
throw new AccessDeniedException();
}
return order;
}
}
// 全局方案:
// 1. 默认拒绝(Deny by Default)
// 2. 最小权限原则
// 3. 服务端验证权限(不信任客户端)
// 4. 使用成熟的 RBAC/ABAC 框架
2. A02:2021 加密机制失效(Cryptographic Failures)
2.1 风险描述
敏感数据(密码、信用卡、PII)因加密不当而被泄露。
2.2 常见漏洞
❌ 明文存储密码
❌ 使用 MD5/SHA1 哈希密码(可被彩虹表破解)
❌ 硬编码密钥在代码中
❌ 使用弱加密算法(DES/RC4)
❌ 不验证 TLS 证书
❌ 在 URL 中传输敏感数据
2.3 防御方案
// ✅ 密码哈希:BCrypt / Argon2 / PBKDF2
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); // strength=12
String hashed = encoder.encode("user_password");
boolean matches = encoder.matches("user_password", hashed);
// ✅ 敏感数据加密(AES-256-GCM)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);
// ✅ HTTPS 强制
server:
ssl:
enabled: true
protocol: TLS
enabled-protocols: TLSv1.2,TLSv1.3
3. A03:2021 注入(Injection)
3.1 SQL 注入
// ❌ 字符串拼接(危险!)
String sql = "SELECT * FROM users WHERE name = '" + username + "'";
// username = "' OR '1'='1" → 绕过认证
// ✅ 预处理语句
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
3.2 其他注入类型
| 类型 | 示例 | 防御 |
|---|---|---|
| NoSQL 注入 | db.users.find({name: {$gt: ''}}) | 参数化查询、输入验证 |
| OS 命令注入 | Runtime.exec("ping " + ip) | 禁止拼接命令、使用 API |
| LDAP 注入 | 恶意过滤条件 | 参数化 LDAP 查询 |
| XPath 注入 | 操纵 XML 查询 | 参数化 XPath |
| Expression 注入 | SPEL/OGNL 注入 | 禁用表达式、沙箱 |
4. A04:2021 不安全设计(Insecure Design)
4.1 风险描述
安全设计缺陷无法通过完美实现来修复,必须在架构阶段考虑安全。
4.2 反例与正例
❌ 不安全设计:
- 业务逻辑中假设用户总是按正常流程操作
- 无业务流校验(跳过步骤、重复提交)
- 缺少安全的需求分析
✅ 安全设计原则:
- 威胁建模(Threat Modeling)
- 安全需求作为功能需求的一部分
- 关键业务步骤状态机校验
- 业务逻辑边界检查
4.3 威胁建模 STRIDE
| 威胁 | 描述 | 示例 |
|---|---|---|
| Spoofing | 身份伪装 | 伪造 JWT Token |
| Tampering | 数据篡改 | 修改请求参数 |
| Repudiation | 否认操作 | 无审计日志 |
| Information Disclosure | 信息泄露 | 返回敏感错误信息 |
| Denial of Service | 拒绝服务 | 慢查询攻击 |
| Elevation of Privilege | 权限提升 | 普通用户变管理员 |
5. A05:2021 安全配置错误(Security Misconfiguration)
5.1 常见错误
❌ 默认密码未修改(admin/admin)
❌ 调试模式在生产环境开启
❌ 不必要的功能/端口/服务暴露
❌ 错误信息泄露敏感信息(堆栈跟踪、SQL语句)
❌ 缺少安全响应头
❌ CORS 配置过于宽松:Access-Control-Allow-Origin: *
5.2 安全基线配置
# Spring Boot 安全配置
server:
error:
whitelabel:
enabled: false # 禁用默认错误页
include-stacktrace: never
include-message: never
management:
endpoints:
web:
exposure:
include: health,info # 不暴露 sensitive endpoints
base-path: /admin/actuator # 隐藏路径
security:
headers:
content-security-policy: "default-src 'self'"
x-content-type-options: nosniff
x-frame-options: DENY
strict-transport-security: max-age=31536000; includeSubDomains
6. A06:2021 易受攻击和过时的组件(Vulnerable and Outdated Components)
6.1 供应链风险
依赖树风险:
Your App
├── dependency A v1.0 (有 CVE-2024-1234)
│ └── dependency B v2.1 (有 CVE-2024-5678)
└── dependency C v3.0 (安全)
Log4j2 事件(2021):流行的日志框架存在 RCE 漏洞
影响范围:几乎所有 Java 应用
6.2 防御方案
# 使用依赖扫描工具
# Maven
mvn org.owasp:dependency-check-maven:check
# npm
npm audit
yarn audit
# Gradle
./gradlew dependencyCheckAnalyze
# Snyk 持续监控
snyk monitor
snyk test
# GitHub Dependabot
# 自动创建 PR 升级有漏洞的依赖
7. A07:2021 身份识别与认证失败(Identification and Authentication Failures)
7.1 常见漏洞
❌ 允许弱密码(123456)
❌ 无多因素认证(MFA)
❌ 会话管理不当(无超时、固定会话 ID)
❌ 凭证恢复/密码重置流程不安全
❌ 使用明文传输凭证
❌ 登录爆破无防护(无验证码、无速率限制)
7.2 安全认证实现
// 登录速率限制(Bucket4j)
Bucket bucket = Bucket.builder()
.addLimit(Bandwidth.classic(5, Refill.intervally(5, Duration.ofMinutes(1))))
.build();
if (!bucket.tryConsume(1)) {
throw new TooManyRequestsException("登录尝试过多,请稍后再试");
}
// JWT 安全配置
@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkUri).build();
decoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuer));
return decoder;
}
// - 使用 RS256(非对称加密)
// - 设置合理的 exp(15分钟)
// - 包含 jti 用于撤销
// - HTTPS only
8. A08:2021 软件和数据完整性失败(Software and Data Integrity Failures)
8.1 风险描述
应用基于不可信来源的假设做出关键决策,如:
- 不安全的反序列化
- 无验证的依赖拉取
- CI/CD 管道被篡改
8.2 不安全的反序列化
// ❌ Java 原生反序列化(危险!)
ObjectInputStream ois = new ObjectInputStream(input);
Object obj = ois.readObject(); // 可执行任意代码!
// ✅ 使用 JSON 替代(Jackson + 类型白名单)
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(
BasicPolymorphicTypeValidator.builder()
.allowIfBaseType(Object.class)
.build(),
ObjectMapper.DefaultTyping.NON_FINAL
);
// 更好的方案:明确类型,不使用多态
User user = mapper.readValue(json, User.class);
8.3 CI/CD 安全
# 验证依赖签名
- name: Verify signatures
run: |
gpg --verify artifact.jar.asc artifact.jar
# SLSA 供应链层级
# Level 1: 构建过程脚本化
# Level 2: 使用版本控制和托管构建服务
# Level 3: 构建环境安全隔离 + 源码完整性
# Level 4: 双人员审查 + 可复现构建
9. A09:2021 安全日志与监控失败(Security Logging and Monitoring Failures)
9.1 为什么重要
“你无法保护你看不见的东西。” 攻击者在被检测前平均在系统中潜伏 280 天(IBM Security Report)。
9.2 应该记录什么
// 安全事件日志模板
@Aspect
@Component
public class SecurityAuditAspect {
@AfterReturning("@annotation(auditable)")
public void audit(JoinPoint jp, Auditable auditable) {
SecurityEvent event = SecurityEvent.builder()
.timestamp(Instant.now())
.user(getCurrentUser())
.action(auditable.action())
.resource(auditable.resource())
.ip(getClientIp())
.userAgent(getUserAgent())
.result("SUCCESS")
.build();
securityLogger.info(event);
}
}
// 必须记录的事件:
// - 登录成功/失败
// - 权限变更
// - 敏感数据访问
// - 管理员操作
// - 配置变更
// - 安全异常(XSS/注入尝试)
10. A10:2021 服务端请求伪造(SSRF)
10.1 攻击原理
攻击者让服务器向攻击者指定的地址发送请求:
场景:图片下载功能
POST /download-image
{ "url": "http://attacker.com/malicious" }
危害:
- 访问内网服务(metadata API、内部管理接口)
- 攻击云厂商 metadata(获取 AK/SK)
- 端口扫描内网
- 读取本地文件(file:///etc/passwd)
10.2 防御方案
public class UrlValidator {
private static final Set<String> ALLOWED_SCHEMES = Set.of("http", "https");
private static final Set<String> BLOCKED_HOSTS = Set.of(
"localhost", "127.0.0.1", "0.0.0.0",
"169.254.169.254" // AWS/阿里云 metadata
);
public static boolean isSafe(String urlString) {
try {
URL url = new URL(urlString);
// 1. 验证协议
if (!ALLOWED_SCHEMES.contains(url.getProtocol())) {
return false;
}
// 2. 解析 IP(防止 DNS 重绑定)
InetAddress address = InetAddress.getByName(url.getHost());
if (address.isLoopbackAddress() || address.isSiteLocalAddress()) {
return false;
}
// 3. 黑名单检查
if (BLOCKED_HOSTS.contains(url.getHost().toLowerCase())) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
}
11. 安全开发生命周期(SDLC)
需求阶段 设计阶段 开发阶段 测试阶段 部署阶段 运维阶段
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
威胁建模 安全设计 安全编码 SAST/DAST 容器扫描 漏洞管理
安全需求 安全架构 代码审计 渗透测试 配置基线 应急响应
数据流图 依赖检查 模糊测试 Secrets扫描 日志监控
12. 总结
OWASP Top 10 是安全入门与评估的基础框架,但真正的安全需要纵深防御:
分层防御模型:
网络层 ──► WAF / CDN / DDoS 防护
│
主机层 ──► OS Hardening / 容器安全 / 运行时防护
│
应用层 ──► 输入验证 / 输出编码 / 访问控制 / 加密
│
数据层 ──► 数据库加密 / 备份 / 审计日志
│
人员层 ──► 安全意识培训 / 最小权限 / 安全编码规范
安全不是一次性任务,而是贯穿软件生命周期的持续活动。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。