必须用动态占位符+参数绑定处理多选IN参数,直接implode拼接字符串进SQL会导致SQL注入;因implode不转义,攻击者可提交恶意字符串如"1,2,5' OR '1'='1",使SQL解析异常并执行任意逻辑。

必须把多选值转成数组交给驱动做批量参数绑定,字符串拼接 IN 条件是高危操作,没有例外。
为什么 implode() 拼接字符串直接进 SQL 就会中招
多选下拉框传来的值常是 "1,2,5" 或 ["1","2","5"],开发者容易写成:"SELECT * FROM user WHERE id IN (" . implode(",", $ids) . ")"。问题在于:
- 哪怕每个元素看着像数字,攻击者可提交
"1,2,5' OR '1'='1",最终 SQL 变成IN (1,2,5' OR '1'='1) -
implode()不做任何转义,整段结果被数据库当作文本字面量解析,不是参数 - ORM 的
whereIn()若传入的是字符串而非数组(比如whereIn('id', "1,2,5")),照样不安全
PHP 中用 PDO 正确处理多选 IN 参数
核心是:把原始输入拆成数组 → 逐个绑定 → 让 PDO 自动处理占位符数量。不能偷懒用 str_replace 或 addslashes。
- 先校验输入是否为合法数组或逗号分隔字符串:
is_array($_POST['ids']) ? $_POST['ids'] : explode(',', $_POST['ids']) - 过滤每个元素为整数或白名单字符串(如只允许
["active","draft"]):array_map('intval', $ids)或array_filter($ids, fn($v) => in_array($v, $whitelist)) - 生成对应数量的问号占位符:
$placeholders = str_repeat('?,', count($ids) - 1) . '?' - 执行时传数组:
$stmt = $pdo->prepare("SELECT * FROM user WHERE id IN ($placeholders)"); $stmt->execute($ids);
Java MyBatis 里别用 ${} 拼接 IN
${} 是字符串替换,#{} 才是预编译参数。拼 IN 时若用 ${ids},等于把用户输入原样塞进 SQL。
- 正确写法是用
foreach标签动态生成占位符:WHERE id IN <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach> - 确保传入的
ids是List<integer></integer>或List<string></string>,不是拼好的字符串 - 如果必须用原生 SQL,就别碰
${}—— 改用PreparedStatement手动构造占位符并setInt()/setString()绑定
容易被忽略的边界情况
修复完主逻辑,这几个点常被跳过,但一出问题就是线上漏洞:
- 空数组没处理:
WHERE id IN ()在 MySQL 会报错,需提前判断if (empty($ids)) { return []; } - 数组元素含空字符串或
null:array_filter($ids, 'strlen')清洗后再用 - 前端传了非预期类型(如对象、嵌套数组),后端没做
is_array()或json_decode()类型校验 - 日志里打印了原始
$_POST['ids']—— 如果含恶意字符串,可能被 WAF 或审计系统误判为攻击载荷











