Spring Security 上线前常见的安全配置错误有哪些?
简化版
常见的上线安全配置错误:① 过度放行路径(调试时 permitAll 后忘收回、anyRequest().permitAll());② 用 Cookie/Session 却关了 CSRF;③ CORS 放太宽(允许任意 Origin 还带凭证);④ 密码用了明文/MD5 等不安全存储;⑤ JWT 校验不完整(只验签名);⑥ 管理端点暴露(Actuator/Swagger 无鉴权);⑦ 错误响应泄露信息(堆栈、内部路径)。上线前应按最小权限原则逐项检查认证、授权(含反向越权场景)、会话、CSRF/CORS、密码/Token、安全响应头、管理端点、日志脱敏。
详细版
上线安全检查清单:
| 类别 | 要检查的错误 |
|---|---|
| 路径放行 | 调试的 permitAll 忘收回、默认放行;应白名单公开路径、其余默认认证 |
| CSRF | Cookie/Session 写接口却关了 CSRF |
| CORS | 允许任意 Origin、还带凭证 |
| 密码 | 明文/可逆加密/MD5-SHA 快哈希 |
| JWT | 只验签名,不验 iss/aud/exp/算法/状态 |
| 管理端点 | Actuator、Swagger、监控接口无鉴权暴露 |
| 认证入口 | API 未认证却返回 HTML 登录页而非 401 |
| 响应/日志 | 错误响应泄露堆栈、日志打印密码/token |
| 响应头/Cookie | 缺 HTTPS、HSTS、Secure/HttpOnly/SameSite |
完整版教学
一、默认策略要保守(白名单)
最常见的错误是放行过度。安全配置应遵循最小权限原则:
- 用白名单——明确列出公开接口(少而明确),其余请求默认需要认证(
anyRequest().authenticated())。 - 不要用黑名单思路(「这几个需要登录、其余随便」)——黑名单容易漏掉新加的路径(新写一个接口忘了配规则就裸奔了)。
上线前最容易遗留的问题:调试时为了「先调通」加的 permitAll、临时禁用的过滤器、开放的 Swagger/Actuator——上线前一定要收回。「临时 permitAll 忘删」是经典事故。
http.authorizeHttpRequests(a -> a
.requestMatchers("/login", "/health").permitAll()
.anyRequest().authenticated());
上线安全配置先问一句:除了明确白名单,其他请求是不是默认需要认证?如果答案不是,风险就很高。
二、认证入口要明确(API 别返回登录页)
不同客户端的认证失败响应应该不同:
- 浏览器页面 → 可以跳转登录页(返回 HTML)。
- REST API → 应该返回 401 JSON(而非 HTML 登录页)。
如果用默认配置混用,API 在未认证时可能返回一个 HTML 登录页面——前端 AJAX 拿到一堆 HTML 无法处理,网关/客户端也难正确响应。所以 API 链要自定义 AuthenticationEntryPoint 返回 401(见「过滤器链」专题)。
三、授权规则要覆盖反向场景(越权)
「只验证管理员能访问」是不够的——越权漏洞往往不是「完全没鉴权」,而是少了数据范围或状态校验。上线前要验证反向场景:
- 普通用户不能访问管理接口(403)。
- A 租户不能访问 B 租户的数据(数据隔离)。
- 被禁用/封禁的用户不能继续操作(状态校验)。
- 用户不能访问别人的数据(如改 URL 里的 id 访问他人订单——IDOR / 越权访问)。
数据级越权是重灾区——order:read 权限不代表能读所有订单,要在查询里加数据范围条件(见「Method Security / RBAC」专题)。
| 检查项 | 正向测试 | 反向测试 |
|---|---|---|
| 管理接口 | 管理员能访问 | 普通用户应 403 |
| 数据范围 | 用户能看自己的数据 | 改 id 访问别人数据应拒绝 |
| 租户隔离 | A 租户访问 A 数据 | A 租户访问 B 数据应拒绝 |
| 账号状态 | 正常账号可操作 | 禁用账号应拒绝 |
四、CSRF 和 CORS 不要粗暴处理
- CSRF:用 Cookie/Session 的写接口应保留 CSRF 防护——别为了「调通」直接
csrf().disable()。只有纯 Bearer Token API(Token 不被浏览器自动携带)才可以按风险关闭(见「CSRF vs CORS」专题)。 - CORS:要明确允许的来源、方法、头、凭证,生产环境不能随意放开任意 Origin,尤其带凭证时
allowedOrigins绝不能是*。「跨域调通」不等于「安全配置完成」——CORS 只是让浏览器允许读响应,不是鉴权。
五、密码和 Token 安全
- 密码:必须用 BCrypt / Argon2 / PBKDF2 这类带盐慢哈希,绝不能明文、可逆加密、或用 MD5/SHA 快摘要(见「PasswordEncoder」专题)。
- JWT:要短有效期 + 完整校验声明(iss/aud/exp/nbf/算法白名单/用户状态)+ 支持密钥轮换——别只验签名(见「JWT 校验」专题)。Refresh Token 要可撤销、轮换、不打印到日志。
六、响应头和 Cookie
启用 HTTPS 后,配置安全响应头和 Cookie 属性降低浏览器攻击面:
- Cookie:Secure(仅 HTTPS)+ HttpOnly(防 JS 读)+ SameSite(防 CSRF)。
- 安全响应头:HSTS(强制 HTTPS)、X-Content-Type-Options: nosniff(防 MIME 嗅探)、X-Frame-Options / CSP frame-ancestors(防点击劫持)等。
安全响应头不是万能的(不能替代认证授权),但能降低常见的浏览器端攻击面(XSS、点击劫持、降级攻击),是纵深防御的一环。Spring Security 默认会加一部分安全头,要确认没被关掉。
七、管理端点与信息泄露
- 管理端点暴露:Actuator(
/actuator/**,尤其/actuator/env、/heapdump会泄露敏感信息)、Swagger/OpenAPI 文档、监控接口——生产环境要么关闭、要么加强鉴权,绝不能裸奔。 - 错误响应泄露信息:异常响应不要暴露堆栈、内部类名、SQL、文件路径给客户端——这些能帮攻击者摸清系统。生产用统一的、脱敏的错误响应。
- 日志脱敏:日志绝不打印密码、token、身份证、银行卡等敏感信息。
- 默认账号/测试开关:删掉默认密码、测试后门、调试开关。
八、常见误区与追问
- 误区:接口能调通就说明安全配置完成。 调通只证明正向路径可用,还必须验证未登录、低权限、跨用户、跨租户等反向场景。
- 误区:生产环境临时开放 Swagger 和 Actuator 问题不大。 这些端点可能泄露接口结构、环境变量、堆信息或监控数据,必须关闭或强鉴权。
- 误区:CORS 放开所有来源可以当作开发便利保留上线。 生产 CORS 应精确白名单,尤其带凭证时不能允许任意来源。
- 追问:为什么 API 要返回 401 JSON 而不是登录页 HTML? API 客户端需要机器可读的认证失败语义,HTML 登录页会导致前端和网关误处理。
- 追问:上线前为什么要查日志脱敏? 密码、token、身份证等进入日志后会在日志系统、备份和排障链路中继续扩散。
- 追问:安全响应头能替代认证授权吗? 不能。它们降低浏览器攻击面,是纵深防御,不是业务访问控制。
九、加强记忆
上线安全按最小权限原则逐项检查:① 路径白名单(公开路径少而明确、其余默认认证,调试的 permitAll 上线前收回,别用黑名单);② 认证入口(API 返回 401 JSON 而非 HTML 登录页);③ 授权覆盖反向场景(普通用户越权、跨租户、被禁用户、改 id 访问他人数据——数据级越权是重灾区);④ CSRF(Cookie/Session 写接口保留,别粗暴 disable)+ CORS(明确来源,带凭证不用 *,跨域调通≠安全);⑤ 密码用 BCrypt(不明文/MD5)+ JWT 完整校验+短过期+可撤销;⑥ 安全响应头/Cookie(HTTPS、HSTS、Secure/HttpOnly/SameSite、防点击劫持/嗅探);⑦ 管理端点(Actuator/Swagger)加鉴权或关闭、错误响应不泄露堆栈、日志脱敏、删默认账号/测试开关。安全加固是发布流程的固定门禁,不是一次配置。