必须使用预编译参数绑定(如PreparedStatement.setBlob)防止BLOB字段SQL注入,字符串拼接或${}方式无论内容是否为二进制均高危;校验文件头等仅防业务滥用,不防注入。

直接拼接二进制流进 SQL 是高危操作,BLOB 类型本身不免疫 SQL 注入——问题不在字段类型,而在于你如何把用户传来的二进制数据(比如图片、PDF、加密密文)塞进 SQL 语句里。只要用了字符串拼接或 ${} 这类非预编译方式构造查询,哪怕内容是 0xFFD8FF,照样可能被绕过。
用 PreparedStatement 绑定 BLOB 参数,别手写拼接
这是唯一可靠的方式。数据库驱动会把二进制数据作为独立参数传输,和 SQL 结构完全隔离。
- Java(JDBC)中必须用
setBlob(int, InputStream)或setBytes(int, byte[]),而不是String.format("INSERT INTO t(v) VALUES ('%s')", bytes) - PHP(PDO)要用
$stmt->bindParam(1, $blobData, PDO::PARAM_LOB),且确保PDO::ATTR_EMULATE_PREPARES设为false(否则会退化为字符串拼接) - MyBatis 中必须用
#{blobData},严禁用${blobData};若需动态表名/列名,必须走白名单校验,不能靠“转义 blob”来补救
校验二进制流的合法性 ≠ 防注入,但能降低误用风险
对 BLOB 内容做格式校验(如 Magic Number、文件头、长度限制)不是防 SQL 注入的手段,而是防止业务逻辑被滥用(例如上传恶意 shell 脚本伪装成图片)。它无法阻止攻击者在合法文件头后追加 SQL 片段并拼接到查询中。
- 校验示例:读取前 4 字节判断是否为 PNG(
89 50 4E 47)、PDF(25 50 44 46) - 必须在绑定前完成校验,且校验逻辑不能依赖未经处理的原始输入(比如先 base64 decode 再校验,decode 过程本身若未限制长度,可能触发 OOM)
- 校验失败应直接拒绝请求,不要尝试“清洗”或截断——二进制流一旦被污染,清洗逻辑极难覆盖所有边界情况
警惕 ORM 和框架的“自动转换”陷阱
有些框架(如旧版 Hibernate、Laravel Eloquent)在处理 byte[] 或 BinaryStream 时,若配置不当或字段映射错误,可能回退到字符串拼接模式。尤其注意以下场景:
- 使用原生 SQL 查询(
@Query(native = true))时,仍需手动绑定BLOB参数,框架不接管 - MyBatis 的
<bind></bind>标签或OGNL表达式若引用了未过滤的二进制字段,可能间接导致拼接 - 日志打印
BLOB内容时若未脱敏(如log.info("blob: {}", bytes)),可能泄露敏感信息或触发日志注入
真正需要盯紧的,从来不是 BLOB 数据长什么样,而是它**以什么方式进入 SQL 执行流程**。参数化是铁律,其余所有校验、过滤、日志脱敏,都是围绕这条线打辅助。漏掉绑定这一步,后面做得再细也白搭。

















