命令注入为什么难修
命令注入的根因是用户输入拼接进了被 shell 解释的字符串:一旦输入里带上分隔符、管道或反引号,命令结构就被改变。修复的核心思路不是「过滤更多字符」,而是让输入根本不经过 shell 解析——优先用语言内置库完成同样的事,必须调用外部程序时以参数数组传递,输入值只作为参数而非命令文本。对确实无法避免的参数,再叠加白名单与规范化校验。
基本信息
| 首选方案 | 用语言内置库替代 shell 命令(如文件操作用标准库) |
|---|---|
| 次选方案 | 以参数数组调用外部程序,不经过 shell 拼接 |
| 兜底措施 | 对文件名、路径、数量类参数做白名单与规范化 |
| 适用语言 | Java ProcessBuilder、Python subprocess 列表形式、PHP 数组形式 |
| 不推荐 | 字符串拼接后交给 shell 解释,包括 sh -c 形式的包装 |
典型使用场景
- 代码审计中定位命令执行调用点的修复方案设计
- 修复评审时判断「过滤特殊字符」是否已足够
- 设计图片处理、文档转换等必须调用外部程序的模块
- 评审运维脚本与定时任务中的参数传递方式
- 判断第三方库内部的命令封装是否引入了新的注入面
调用方式对比
调用方式对比
不安全:把输入拼进命令字符串交给 shell 解释
参数值中的分隔符、管道与替换语法会改变命令结构
安全: 以参数数组调用,输入只作为独立参数传入
例如 ['convert', 源文件, 目标文件],不经过 shell
更强: 用语言内置库完成同样功能,彻底消除外部程序调用
兜底: 对参数做白名单校验,并拒绝控制字符与路径穿越写法修复方案对照
| 方案 | 有效性 | 注意点 |
|---|---|---|
| 改用语言内置库 | 高 | 需要确认功能等价,注意字符编码与超时处理 |
| 参数数组调用 | 高 | 不能同时使用 shell=True 之类的开关 |
| 白名单校验参数值 | 中 | 依赖校验完整,只适合取值可枚举的场景 |
| 黑名单过滤特殊字符 | 低 | 编码、通配符与换行等写法多样,容易绕过 |
黑名单过滤特殊字符不可靠:不同 shell、不同编码与不同调用层次对字符的解释都不一样,应从根本上避免拼接。
外部程序自带解析的场景要单独确认:某些工具会把参数当成命令串再解析一次,这类「二次解析」需要按工具文档禁用或严格校验。