为什么SQL查询不能直接用拼接的Cookie值来防注入?

2026-04-29 01:272阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计960个文字,预计阅读时间需要4分钟。

为什么SQL查询不能直接用拼接的Cookie值来防注入?

直接将 `Cookie` 中的值(例如 `session_id=abc123` 或 `user_token=xyz789`)用字符串拼接进 SQL 查询,等同于让攻击者任意控制查询逻辑。这并非可能被注入,而是必然可注入——因为 HTTP Cookie 完全由客户端控制,服务端未进行验证和参数化,所以任何客户端提供的 Cookie 值都会被直接用于查询,没有任何区分。

SELECT * FROM users WHERE token = ' + cookie_value + ' 是典型高危写法

这种拼接常见于老式登录校验或会话查找逻辑,例如在 Node.js 中用 mysql 模块、PHP 中用 mysqli::query、Java 中用 Statement.execute 等非参数化方式构造 SQL。

攻击者只需在 Cookie 中传入 user_token=' OR '1'='1,整条语句就变成:

SELECT * FROM users WHERE token = '' OR '1'='1'

结果是匹配全部用户,甚至配合注释符 -- 或分号 ; 还能执行多语句(取决于驱动是否启用多语句模式)。

  • 即使 Cookie 值经过 base64 编码或简单正则过滤(如只允许字母数字),也挡不住绕过:base64 解码后仍是可控字符串,正则漏掉 ';/* 等关键字符很常见
  • 部分框架对 Cookie 做了签名(如 Express 的 signedCookies),但签名只防篡改,不解决拼接本身的问题——一旦签名密钥泄露或验证逻辑被绕过,照样崩
  • ORM 如 Sequelize、TypeORM 默认也不自动防御拼接;若手动拼进 where 字段(如 where: `token = '${req.cookies.token}'`),照样中招

唯一安全做法:所有外部输入走参数化查询或预编译语句

无论来源是 URL、Body 还是 Cookie,只要是动态值,就必须脱离 SQL 字符串结构。

  • Node.js(pg):用 client.query('SELECT * FROM users WHERE token = $1', [req.cookies.token])
  • Python(psycopg2):用 cursor.execute("SELECT * FROM users WHERE token = %s", (request.cookies.get('token'),))
  • PHP(PDO):用 $stmt = $pdo->prepare("SELECT * FROM users WHERE token = ?"); $stmt->execute([$token]);
  • Java(JDBC):必须用 PreparedStatement,绝不用 Statement 拼接

注意:参数化只保护「值」,不保护「标识符」(如表名、列名)。如果 Cookie 决定了查哪张表(如 tenant_id 动态选库),那得用白名单映射,不能拼接表名。

Cookie 值本身还要额外校验长度、格式和时效性

参数化解决注入,但不解决业务逻辑漏洞。比如 user_token 长度异常(500 字符)、含非法字符(控制符、Unicode 零宽空格)、时间戳明显过期,这些都该在进 SQL 前拦截。

  • crypto.timingSafeEqual()(Node)或 hash_equals()(PHP)比对 token,防止时序攻击
  • 查库前先检查 token 是否在 Redis 黑名单里(如用户登出后未失效)
  • 避免把敏感字段(如密码哈希、密钥)直接存进 Cookie;应只存不可逆的随机令牌,并与服务端 session 绑定

最常被忽略的一点:开发时习惯用 Postman 或浏览器插件手动设 Cookie 测试,容易忘记它和真实攻击面完全一致——那个你随手填的 admin=true,就是攻击者第一批尝试的 payload。

本文共计960个文字,预计阅读时间需要4分钟。

为什么SQL查询不能直接用拼接的Cookie值来防注入?

直接将 `Cookie` 中的值(例如 `session_id=abc123` 或 `user_token=xyz789`)用字符串拼接进 SQL 查询,等同于让攻击者任意控制查询逻辑。这并非可能被注入,而是必然可注入——因为 HTTP Cookie 完全由客户端控制,服务端未进行验证和参数化,所以任何客户端提供的 Cookie 值都会被直接用于查询,没有任何区分。

SELECT * FROM users WHERE token = ' + cookie_value + ' 是典型高危写法

这种拼接常见于老式登录校验或会话查找逻辑,例如在 Node.js 中用 mysql 模块、PHP 中用 mysqli::query、Java 中用 Statement.execute 等非参数化方式构造 SQL。

攻击者只需在 Cookie 中传入 user_token=' OR '1'='1,整条语句就变成:

SELECT * FROM users WHERE token = '' OR '1'='1'

结果是匹配全部用户,甚至配合注释符 -- 或分号 ; 还能执行多语句(取决于驱动是否启用多语句模式)。

  • 即使 Cookie 值经过 base64 编码或简单正则过滤(如只允许字母数字),也挡不住绕过:base64 解码后仍是可控字符串,正则漏掉 ';/* 等关键字符很常见
  • 部分框架对 Cookie 做了签名(如 Express 的 signedCookies),但签名只防篡改,不解决拼接本身的问题——一旦签名密钥泄露或验证逻辑被绕过,照样崩
  • ORM 如 Sequelize、TypeORM 默认也不自动防御拼接;若手动拼进 where 字段(如 where: `token = '${req.cookies.token}'`),照样中招

唯一安全做法:所有外部输入走参数化查询或预编译语句

无论来源是 URL、Body 还是 Cookie,只要是动态值,就必须脱离 SQL 字符串结构。

  • Node.js(pg):用 client.query('SELECT * FROM users WHERE token = $1', [req.cookies.token])
  • Python(psycopg2):用 cursor.execute("SELECT * FROM users WHERE token = %s", (request.cookies.get('token'),))
  • PHP(PDO):用 $stmt = $pdo->prepare("SELECT * FROM users WHERE token = ?"); $stmt->execute([$token]);
  • Java(JDBC):必须用 PreparedStatement,绝不用 Statement 拼接

注意:参数化只保护「值」,不保护「标识符」(如表名、列名)。如果 Cookie 决定了查哪张表(如 tenant_id 动态选库),那得用白名单映射,不能拼接表名。

Cookie 值本身还要额外校验长度、格式和时效性

参数化解决注入,但不解决业务逻辑漏洞。比如 user_token 长度异常(500 字符)、含非法字符(控制符、Unicode 零宽空格)、时间戳明显过期,这些都该在进 SQL 前拦截。

  • crypto.timingSafeEqual()(Node)或 hash_equals()(PHP)比对 token,防止时序攻击
  • 查库前先检查 token 是否在 Redis 黑名单里(如用户登出后未失效)
  • 避免把敏感字段(如密码哈希、密钥)直接存进 Cookie;应只存不可逆的随机令牌,并与服务端 session 绑定

最常被忽略的一点:开发时习惯用 Postman 或浏览器插件手动设 Cookie 测试,容易忘记它和真实攻击面完全一致——那个你随手填的 admin=true,就是攻击者第一批尝试的 payload。