SQL注入防御需结合会话层安全,仅靠PreparedStatement不足;必须在Controller/Service层校验资源归属,禁用客户端可篡改的session字段,强化session存储与传输安全,并覆盖越权测试。

SQL注入防御不能只靠预处理,会话层漏洞照样拖后腿
光用 PreparedStatement 或 query 参数化,挡不住攻击者复用合法会话、篡改 session 数据后绕过权限校验再发恶意 SQL。这类逻辑缺陷的根因不在数据库访问层,而在会话状态与业务权限的耦合松散。
为什么 session_id 有效 ≠ 当前请求有权执行该 SQL
常见错误是:登录后生成 session,后续所有接口只校验 session_id 是否存在且未过期,却不校验「当前用户身份是否被授权操作本次请求中的资源 ID」。比如一个订单查询接口接收 order_id 参数,后端直接拼进 SQL:SELECT * FROM orders WHERE id = ? AND user_id = ? —— 这里第二个 ? 如果取自 session 中的 user_id,而该值可被攻击者在客户端篡改(如 Cookie 中伪造 JSON 或 Base64 解码修改),就构成典型会话劫持+越权+SQL 注入三重风险。
- 不要信任任何来自客户端的 session 衍生字段(
user_role、tenant_id、org_code)—— 它们必须在每次关键请求中从可信存储(如 Redis + 加密签名的 session token)重新加载并验证 - 避免把权限判断延迟到 DAO 层;应在 Controller 或 Service 入口完成「资源归属校验」,校验失败立即返回 403,不进入数据库操作
- 若使用 JWT,别只验 signature 和 expiry;必须校验
aud(目标服务)、iss(签发方),且 payload 中的sub和scope需与本次请求上下文强绑定
Session 存储选型直接影响防篡改能力
把 session 存在客户端(Cookie 中明文或弱加密)等于给攻击者递刀。Node.js 用 express-session 默认配 memoryStore?开发环境可以,上线必须切到 redisStore 或 postgresStore,否则重启进程 session 全丢,逼你加“记住我”逻辑,反而引入持久化 token 管理漏洞。
-
HttpOnly+Secure+SameSite=Strict是 Cookie 的底线配置,少一个都可能让 XSS 借助 document.cookie 泄露 session - Redis 中存 session 时,key 建议用
session:{hash(session_id)}而非裸session_id,防批量扫描 - 不要在 session value 里塞完整用户对象;只存最小必要字段(如
user_id、auth_at),其余查库获取,避免脏数据或越权缓存
测试会话逻辑缺陷比写 SQL 注入测试更难发现
自动化扫描工具(如 sqlmap)几乎无法触发这类问题,因为它依赖真实会话上下文和业务规则。真正有效的办法是人工构造边界请求:用 A 用户登录拿到 session,手动改 Cookie 或 Header 中的 X-User-ID,再请求 B 用户的资源接口,看是否返回数据或报错信息泄露(如 "Order not found for user 123" 暗示 ID 存在但归属不对)。
- 所有涉及用户隔离的接口,必须有对应越权测试用例,覆盖:ID 为负数、超大整数、UUID 格式乱填、已删除用户 ID
- 日志中禁止打印原始 session 内容或解密后的 JWT payload;记录应仅含
session_hash和操作结果(success/fail) - 生产环境开启 session 变更检测(如 IP 突变、User-Agent 大幅变更),触发二次验证或自动失效旧 session
会话控制不是加个 middleware 就完事;它要求每个业务接口都明确回答一个问题:“此刻这个 session,凭什么能动这条数据?” 答不上来,或者答案依赖客户端输入,就是缺口。

















