跳到内容

4.2 服务端输入边界:注入、路径与文件处理

注入发生在一个解释器无法区分“应用编写的语法”和“外部提供的数据”时。SQL 只是最熟悉的一种;shell、模板、LDAP、日志查询和表达式语言都有相同结构。

参数化查询把值与语法分开

危险代码:

text
"SELECT * FROM users WHERE email = '" + email + "'"

正确方向是使用数据库 driver 的 bind parameter:

sql
SELECT id, password_hash
FROM users
WHERE email = ?

参数只代表值,不能改变查询结构。参数化通常不能绑定表名、列名和排序方向;这些结构性选项应由服务端 enum 映射:

text
sort=recent → ORDER BY created_at DESC
sort=name   → ORDER BY display_name ASC

不要对用户传入的列名做“转义后拼接”。存储过程只有在内部也不动态拼 SQL 时才安全,ORM 也可能通过 raw query、字符串 filter 或不安全 escape hatch 重新引入注入。

数据库账户还应最小权限:读接口不使用 schema owner,报表任务不拥有写权限,应用不能读取其他租户 schema。WAF 可以挡住部分已知 payload,但无法替代码与数据分离。

Shell 命令优先不经过 Shell

危险:

text
shell("convert " + uploadedPath + " output.png")

优先使用库 API;必须调用进程时,用参数数组并关闭 shell 解析:

text
execve("/usr/bin/convert", ["convert", validatedInput, outputPath], cleanEnv)

参数数组降低命令分隔符注入,却不自动保证目标程序的参数安全。以 - 开头的文件名可能被当选项,某些工具支持从 URL、配置或响应文件读取更多输入。应使用 -- 终止选项、服务端生成路径、严格 allowlist,并以低权限 sandbox 执行。

模板要区分展示模板与代码模板

把用户输入作为模板数据通常可以安全转义;让用户控制模板源码则可能形成 Server-Side Template Injection,读取进程对象、环境变量甚至执行命令。

text
render(trustedTemplate, { displayName: untrustedValue })  ✓
render(untrustedTemplateSource, model)                    高风险

确需用户模板时,使用为不可信模板设计的受限语言、固定函数集合、资源限制与进程隔离。仅删除几个危险关键字不能建立沙箱。

路径规范化不能靠字符串前缀

下载接口常见问题:

text
/download?file=../../etc/passwd

安全设计优先让客户端提交不透明对象 ID,由服务端数据库映射到存储对象,而不是接受文件系统路径。若必须处理相对路径,应:

  1. 对输入做一次明确解码,拒绝 NUL 和非法形式;
  2. 在固定根目录下解析 canonical path;
  3. 确认结果仍在根目录内,注意路径分隔符与大小写规则;
  4. 防止符号链接与检查后替换的 TOCTOU;
  5. 使用目录 fd 和安全 open flags 等平台能力缩短竞态。

简单的字符串 startsWith(root) 会把 /srv/uploads-evil 误当成 /srv/uploads 内部路径。

文件上传是内容处理管线

扩展名和浏览器 Content-Type 都由客户端控制。上传服务应组合:

  • 限制大小、数量、压缩展开比和处理时间;
  • 检查允许类型的 magic bytes 与完整解析;
  • 服务端生成对象名,存到 Web root 之外或独立对象存储;
  • 下载时设置安全 Content-TypeContent-Dispositionnosniff
  • 图片、文档和媒体转码放入低权限、有限资源的 sandbox;
  • 恶意软件扫描作为附加信号,不作为唯一安全边界;
  • 对公开内容使用独立 origin,避免上传 HTML 获得主站权限。

Zip Slip、解压炸弹和解析器漏洞说明“只存文件不执行”并不等于安全。

不可信反序列化会恢复行为

某些对象序列化格式可以指定类型、构造器或 gadget chain。不要反序列化来自 Cookie、队列、缓存或上传文件的不可信原生对象图。优先使用只表达数据的 schema 格式,显式允许字段与类型,并限制深度、长度和重复结构。

签名只能证明数据来自持有密钥的一方;如果多个低信任服务共享签名密钥,其中任一服务仍可能构造危险对象。安全格式和最小解析能力不能由签名替代。

异常路径也要保持边界

解析失败时不要回退到“兼容模式”执行原字符串,也不要把数据库错误、命令 stderr、绝对路径和 stack trace 返回给外部用户。记录内部 correlation ID,向客户端提供稳定错误模型,并对异常率告警。

OWASP Top 10:2025 把 Mishandling of Exceptional Conditions 单列为 A10,提醒安全设计不能只覆盖成功路径。

参考资料

Built with VitePress | Software Systems Atlas