Web 安全基础
OWASP Top 10
OWASP(Open Web Application Security Project)每 3-4 年发布一次 Web 应用十大安全风险:
| 排名 | 风险 | 简要描述 |
|---|---|---|
| A01 | 权限控制失效 | 越权访问、未授权操作 |
| A02 | 加密机制失效 | 敏感数据明文传输/存储、弱加密 |
| A03 | 注入 | SQL/NoSQL/OS/命令注入 |
| A04 | 不安全设计 | 缺乏安全设计模式 |
| A05 | 安全配置错误 | 默认配置、调试接口暴露 |
| A06 | 易受攻击组件 | 第三方库已知漏洞 |
| A07 | 认证与身份管理失效 | 弱密码、会话固定 |
| A08 | 数据完整性失效 | 反序列化、CI/CD 篡改 |
| A09 | 安全日志与监控不足 | 缺乏审计与告警 |
| A10 | SSRF | 服务端请求伪造 |
XSS(跨站脚本攻击)
类型
| 类型 | 特点 | 示例 |
|---|---|---|
| 反射型 | 非持久化,URL 参数注入 | ?q=<script>alert(1)</script> |
| 存储型 | 持久化,存储在服务端 | 评论区写入恶意脚本 |
| DOM 型 | 客户端 JS 动态渲染导致 | innerHTML 直接拼接受害者输入 |
防御
java
// 1. 输出编码(HTML 上下文)
StringEscapeUtils.escapeHtml4(input)
// 2. Content-Security-Policy 头
response.setHeader("Content-Security-Policy",
"default-src 'self'; script-src 'self'")
// 3. 设置 HttpOnly Cookie
cookie.setHttpOnly(true)
// 4. 输入验证(白名单)
if (!input.matches("^[a-zA-Z0-9_]+$")) {
throw new ValidationException("非法输入")
}CSRF(跨站请求伪造)
原理
用户在已登录 A 站的情况下,访问恶意 B 站,B 站伪造请求让用户在不知情下执行 A 站的敏感操作。
防御
| 方式 | 说明 |
|---|---|
| CSRF Token | 表单中嵌入 Token,服务端校验 |
| SameSite Cookie | SameSite=Strict / Lax |
| Referer/Origin 校验 | 检查请求来源 |
| 二次验证 | 支付/修改密码等核心操作要求输入验证码 |
java
// Spring Security 默认启用 CSRF 保护
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)SQL 注入
原理
攻击者通过在输入中嵌入 SQL 语句,改变 SQL 语义。
防御
java
// ❌ 危险:字符串拼接
String sql = "SELECT * FROM users WHERE id = " + id;
// ✅ 安全:参数化查询
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?"
);
ps.setLong(1, id);
// ✅ MyBatis 使用 #{}(预编译),避免 ${}
// 正确:WHERE id = #{id}
// 危险:WHERE id = ${id}其他注入
| 类型 | 防御 |
|---|---|
| NoSQL 注入 | 参数化查询、输入校验 |
| OS 命令注入 | 避免 Runtime.exec,使用白名单 |
| LDAP 注入 | 转义特殊字符 |
| XML 注入 | 避免拼接 XML,使用 XML 解析器 |
SSRF(服务端请求伪造)
原理
攻击者利用服务端发起请求的功能,访问内网或受限资源。
防御
java
// 1. URL 白名单
if (!url.startsWith("https://trusted-api.example.com")) {
throw new SecurityException("非法请求地址");
}
// 2. 禁用重定向
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setInstanceFollowRedirects(false);
// 3. 内网地址黑名单
InetAddress addr = InetAddress.getByName(host);
if (addr.isSiteLocalAddress() || addr.isLoopbackAddress()) {
throw new SecurityException("禁止访问内网地址");
}