SECURE CODING · 攻防 / 编码红线
安全编码速查
从注入防御、XSS 与 CSRF,到认证会话、敏感数据、依赖配置与文件反序列化,安全编码红线逐条成表,写代码与评审时对照即查。
8速查小节
52速查条目
3种 XSS 类型
5个安全响应头
📖 速查表
点击展开各小节
💉 注入类防御
| 攻击类型 | 成因 | 防御要点 |
|---|---|---|
| SQL 注入 | 用户输入被拼进 SQL 语句,改变原语句语义 | 预编译与参数化(PreparedStatement / #{} 占位符);数据库账号最小权限,禁止字符串拼接 红线 |
| MyBatis ${} 陷阱 | ${} 是字符串直接替换,输入照单全收 | 传参一律 #{};动态表名 / 排序字段走白名单映射 易错 |
| 命令注入 | 拼接字符串调用 shell,分号 / 管道符注入额外命令 | 避免拼接执行系统命令;ProcessBuilder 参数数组传参 + 可执行项白名单 高危 |
| SSTI 模板注入 | 用户输入被当作模板内容渲染执行 | 模板与数据分离:用户输入只作变量值,永不作模板源 |
| XXE | XML 解析器允许外部实体与 DTD,读取文件或发起内网请求 | 关闭 DTD 与外部实体(disallow-doctype-decl 等特性);非必要不解析 XML |
| 通用红线 | 一切外部输入(参数 / Header / 文件)默认不可信 | 白名单校验 + 参数化传递;错误信息不回显 SQL 与堆栈细节 |
🧨 XSS 与 CSRF
| XSS 类型 | 注入位置 | 防御要点 |
|---|---|---|
| 存储型 XSS | 恶意脚本入库(评论 / 昵称),他人浏览页面时触发,影响面最大 | 入库校验 + 输出编码;富文本走白名单过滤,只保留安全标签 |
| 反射型 XSS | 参数携带脚本被服务端原样回显到页面 | 输出编码 + 参数严格校验,URL 参数不直接写入 HTML |
| DOM 型 XSS | 前端脚本把不可信数据写入 innerHTML 或经 eval 执行 | 用 textContent 赋值,禁用 eval 与 document.write |
| 手段 | 说明 | 要点 |
|---|---|---|
| 输出编码(按上下文) | HTML 正文 / 属性 / JS / URL 各用对应编码器 | OWASP Java Encoder;数据拼接进页面前一律编码 |
| CSP 响应头 | 限制脚本与资源来源,注入成功也难以执行 | script-src 'self' + nonce;是兜底手段,不能替代输出编码 |
| HttpOnly Cookie | 会话 Cookie 禁止 JS 读取 | 配合 Secure;XSS 即便得手也偷不走会话凭证 |
| CSRF Token | 表单 / 请求头携带随机 Token,服务端比对一致性 | Token 与会话绑定;POST / PUT / DELETE 全覆盖 |
| SameSite Cookie | 限制跨站请求携带 Cookie,从源头削弱 CSRF | Strict 最严格,Lax 兼容大多数跳转场景 |
| 关键操作二次确认 | 转账 / 改密等敏感操作要求验证码或原密码 | 同时缓解 CSRF 与会话被劫持后的破坏面 推荐 |
🔑 认证与会话
认证是系统的正门:正门守不住,后面所有防御都在裸奔。
| 措施 | 正确做法 | 常见错误 |
|---|---|---|
| 密码存储 | bcrypt / argon2 慢哈希 + 随机盐,成本可调 | 用 MD5 / SHA1 甚至明文存库 红线 |
| 会话固定防护 | 登录成功后重建并轮换 SessionId | 沿用登录前旧 SessionId,给会话固定攻击留门 |
| 水平越权 IDOR | 服务端校验资源归属:订单 / 资料 ID 是否属于当前用户 | 只靠前端隐藏入口,改 ID 即可越权 高频漏洞 |
| 垂直越权 | 按角色统一鉴权,拦截器 / 注解收口,默认拒绝 | 仅前端隐藏管理菜单,接口裸奔 |
| 登录限流 | 失败计数 + 图形 / 短信验证码,账号与 IP 双维度限制 | 对暴力破解完全不设防 |
| JWT 安全 | 短有效期 + refresh token 轮换 + 注销黑名单 | 把敏感信息放 payload——Base64 只是编码不是加密 |
| 多因素认证 | 高价值操作叠加 OTP / 短信因子 | 单一密码保护支付类账号 建议 |
🗄️ 敏感数据
| 场景 | 要求 |
|---|---|
| 传输加密 | 全站 HTTPS,禁明文 HTTP;内网服务间调用同样加密,防链路嗅探 |
| 存储加密与密钥管理 | 身份证 / 银行卡等字段级加密(AES-GCM);密钥交 KMS 或配置中心托管,禁止硬编码进代码库 |
| 最小化收集 | 只收集业务必需字段,超期即删;展示层默认脱敏,明文只在必要节点短暂出现 |
| 日志脱敏清单 | 规则 | 示例 |
|---|---|---|
| 手机号 | 保留前 3 后 4 | 138****5678 |
| 身份证号 | 保留前 6 后 4 | 110101********1234 |
| 银行卡号 | 仅保留后 4 位 | ************1234 |
| 邮箱 | 用户名打码,保留域名 | a***@domain.com |
| Token / 密码 / 密钥 | 一律不打日志;异常对象先脱敏再 toString | 日志出现 Bearer eyJ... 即为事故 红线 |
📦 依赖与配置
| 主题 | 要点 |
|---|---|
| 依赖漏洞扫描 | OWASP Dependency-Check / npm audit / Snyk 接入 CI,高危依赖阻断合并并及时升级 |
| 默认配置风险 | 修改中间件默认口令(Redis / Druid / MQ 管理台),关闭示例页面与匿名访问 常见突破口 |
| 调试端点暴露 | 生产关闭 Swagger / Actuator / Druid 监控,或加鉴权并限内网访问 |
| 错误信息泄露 | 统一异常处理返回通用错误码,不外抛堆栈、SQL、内网路径与版本信息 |
| 安全响应头 | 作用 | 示例值 |
|---|---|---|
| Strict-Transport-Security | 强制浏览器后续访问走 HTTPS | max-age=31536000; includeSubDomains |
| X-Frame-Options | 防点击劫持,禁止被 iframe 嵌套 | DENY 或 SAMEORIGIN |
| X-Content-Type-Options | 禁止 MIME 嗅探,按声明类型处理响应 | nosniff |
| Referrer-Policy | 限制 referer 携带的来源信息 | strict-origin-when-cross-origin |
| Permissions-Policy | 收敛摄像头 / 定位等浏览器敏感能力 | camera=(), geolocation=() |
📤 文件与反序列化
文件上传与反序列化是 RCE 高发地:默认全部不可信,白名单先行。
| 风险点 | 防御要点 |
|---|---|
| 上传类型校验 | 校验文件魔数(文件头)而非只看后缀与 Content-Type;类型走白名单 红线 |
| 大小与数量限制 | 单文件与请求体设上限;压缩包防 zip bomb 解压爆炸 |
| 存储隔离 | 随机重命名 + 独立域名 / OSS 托管;上传目录禁执行权限,防脚本被解析 |
| 下载与路径安全 | 固定根目录并校验规范化路径(realpath),防 ../ 目录穿越读取任意文件 |
| Java 原生反序列化 | 禁止 ObjectInputStream 处理不可信数据;升级组件修复已知 gadget 利用链 |
| JSON 自动化绑定 | 多态反序列化收紧 type 白名单;Jackson 开 defaultTyping 必须配校验器兜底 |
| SSRF 防御 | 协议白名单(仅 http/https)、禁内网网段与云元数据地址、禁跟随重定向;DNS 解析后复检目标 IP |
🔐 Java 安全编码片段
三段可直接抄进项目的高频安全写法:预编译防注入、真随机数、防 ReDoS 的正则
// ① 防注入:PreparedStatement 预编译,用户输入永远走占位符
String sql = "SELECT id, name FROM user WHERE phone = ? AND status = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, phone); // 输入只作参数值,不参与 SQL 语义拼接
ps.setInt(2, STATUS_NORMAL);
ResultSet rs = ps.executeQuery();
}
// 对应 MyBatis:传参一律 #{};${} 仅限白名单映射的表名 / 排序字段
// ② 真随机:Token / 验证码 / 盐必须用 SecureRandom,禁止 java.util.Random
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[32];
random.nextBytes(bytes);
String token = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
// Random 是线性同余算法,观测几个输出即可预测后续,验证码形同虚设
// ③ 防 ReDoS:正则避免嵌套量词,白名单字符集 + 长度上限双保险
// ❌ (a+)+$ 、(a|a)*$ —— 嵌套量词,恶意输入触发指数级回溯,CPU 被打满
Pattern SAFE_NAME = Pattern.compile("^[a-zA-Z0-9_\\-]{1,64}$");
if (!SAFE_NAME.matcher(username).matches()) {
throw new IllegalArgumentException("用户名格式非法");
}
🧨 供应链投毒防范
投毒攻击盯的是「你不看的东西」:传递依赖、构建脚本、陌生新包;五道防线层层设卡
| 防线 | 做法 | 要点 |
|---|---|---|
| 锁版本 + lockfile 审查 | 提交并锁定 lockfile / 固定版本号,diff 中出现依赖变更必须人工审查 | 重点盯冒出来的陌生传递依赖:谁引入的、为何升级 第一道闸 |
| 依赖最小化 | 能用 JDK / 框架自带能力就不引第三方包,定期清理不再使用的依赖 | 每个依赖都是攻击面与维护成本:少一个依赖少一份风险 |
| 私服白名单 | 构件只从公司私服(Nexus / Artifactory)拉取,私服仅代理白名单公共仓库 | 禁直连公共源;本地开发也统一走私服,杜绝绕行 |
| CI 跑依赖漏洞扫描 | OWASP Dependency-Check / Snyk 接入流水线,高危 CVE 阻断合并 | 告警必须闭环:升级修复,或在 suppress 文件里写明理由 门禁 |
| 新包先查履历 | 引入前查发布历史、维护者数量、下载量与 issue 响应活跃度 | 刚发布就蹭热点、单一维护者、名字仿冒热门包 = 高危信号 查三代 |