cookie字段本身不会直接触发sql注入,但若将$_cookie值未经校验拼入sql查询(如$user_role、$user_id),则与get/post同属高危输入源;因其常被误认为“可信”而忽略验证,实际易遭篡改,必须按语义做白名单校验或类型强制转换。

Cookie 字段本身不会直接触发 SQL 注入,但一旦你把 $_COOKIE 中的值拼进 SQL 查询(比如用作用户 ID、会话标识、偏好筛选条件),它就和 $_GET、$_POST 一样危险——不加处理就是裸奔。
为什么 Cookie 值容易被忽略做参数化
开发者常默认“Cookie 是自己写的、可信的”,但实际中:
– Cookie 可被浏览器插件、Fiddler/Charles、curl 手动篡改
– 登录态 token、用户角色字段、语言偏好等常存于 Cookie,又常被用于 WHERE 条件
– 框架自动解包后,$_COOKIE['user_role'] 看起来像普通变量,容易被无意识拼接进 SQL
典型错误写法:
$role = $_COOKIE['user_role']; $sql = "SELECT * FROM users WHERE role = '$role'"; // 危险!
攻击者只要设 user_role=administrator' -- ,就能绕过权限检查。
必须对 $_COOKIE 做输入验证,不能只信来源
验证不是“过滤掉单引号”这种低效操作,而是明确字段语义后做类型/范围约束:
- 若期望是固定枚举值(如
user_role只能是admin、member、guest),用白名单校验:if (!in_array($_COOKIE['user_role'], ['admin', 'member', 'guest'])) { die('Invalid role'); } - 若期望是整数 ID(如
user_id),强制转为 int 并检查非零:$uid = (int)$_COOKIE['user_id']; if ($uid
- 若字段含字母数字组合(如 token),限制长度并正则匹配:
if (!preg_match('/^[a-zA-Z0-9]{32,64}$/', $_COOKIE['token'])) { die('Invalid token format'); }
Header 信息(如 User-Agent、Referer)更不能进 SQL
这些字段完全由客户端控制,且内容不可预测。历史上已有真实案例:攻击者在 User-Agent 里塞 ' OR 1=1 -- ,配合日志分析功能导致后台 SQL 报错泄露表结构。
关键原则:
-
$_SERVER['HTTP_USER_AGENT']、$_SERVER['HTTP_REFERER']、$_SERVER['HTTP_X_FORWARDED_FOR']等一律禁止出现在 SQL 字符串拼接中 - 如果真要记录,只允许存入日志表(且字段类型为
TEXT,不参与查询逻辑),或先做htmlspecialchars()+ 截断(如 255 字符) - 绝对不要为了“方便调试”而临时把 Header 值写进 WHERE 或 ORDER BY —— 这类临时代码极易遗漏清理
唯一安全的兜底方案:所有外部输入都走参数化查询
无论数据来自 $_GET、$_POST、$_COOKIE 还是 $_SERVER,只要进 SQL,就必须用预处理:
$stmt = $pdo->prepare("SELECT * FROM users WHERE role = ? AND status = ?");
$stmt->execute([$_COOKIE['role'], $_SERVER['HTTP_X_STATUS'] ?? 'active']);
注意点:
- MySQLi 和 PDO 都支持,但
mysql_query()已废弃且无法参数化,必须替换 - MyBatis 中用
#{},不用${};JDBC 用PreparedStatement,不用Statement - 动态表名、列名无法参数化,此时只能靠白名单硬编码,不能从任何外部输入取值
Header 和 Cookie 的特殊性在于它们更隐蔽、更易被当成“内部数据”放松警惕。真正难防的不是技术,是开发时那一秒的想当然。










