os/exec.command本身不防命令注入,关键在参数传递方式:经shell执行(如sh -c)必危;直接传参仍需校验空格、通配符、路径穿越及命令名白名单,并配合降权隔离才真正安全。

os/exec.Command 本身不防命令注入,关键在怎么传参——只要经过 shell(比如 sh -c),哪怕只拼一个空格,就等于开门放狼。
为什么 os/exec.Command("ls", userInput) 看似安全却可能翻车
表面上看,os/exec.Command 不走 shell,参数是切片,不会解析 ; 或 $()。但隐患藏在两处:
-
userInput含空格或通配符(如/tmp/*.log)时,操作系统可能在 exec 前做 glob 展开,而 Go 不干预这个过程 - 更常见的是路径穿越:
../etc/passwd这类输入若未经filepath.Clean和根目录白名单校验,会直接逃逸到敏感路径 - 某些环境里
ls是 alias 或 wrapper 脚本,实际执行的仍是 shell —— 你控制不了底层
必须禁用 sh -c 的所有变体
任何把用户输入塞进 sh -c "cmd " + userInput 的写法,无论加不加引号、转义不转义,都等同于裸奔。连 bash -c、dash -c、python -c 都得一并拉黑。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- ❌ 危险模式:
exec.Command("sh", "-c", "grep -r "+pattern+" "+dir) - ✅ 安全替代:
exec.Command("grep", "-r", pattern, dir)(pattern和dir必须先校验) - ⚠️ 注意:
exec.LookPath("sh")返回 nil 并不表示安全——攻击者可能用/bin/dash或/usr/bin/python3绕过
动态参数场景下怎么保命
比如解压用户上传的 tar 包到指定目录,既要支持任意子路径,又要防跳出。核心是三步:绝对化 → 清理 → 白名单。
- 用
filepath.Abs(dir)把相对路径转成绝对路径 - 用
filepath.Clean(absDir)消除..和重复斜杠 - 用
strings.HasPrefix(cleaned, "/var/data/")确保结果落在允许前缀下(注意结尾加/防止/var/data2误匹配) - 最终命令写成:
exec.Command("tar", "-xf", archivePath, "-C", safeDestDir, "--no-same-owner")
生产环境别只靠 Go 层拦截
代码里加白名单能拦住大部分,但绕过手段太多:LD_PRELOAD、/proc/self/exe 替换、容器内挂载恶意二进制……真正靠谱的是降权+隔离。
- 运行进程时用非 root 用户,且
chown掉无关目录权限 - Kubernetes 场景下设
securityContext.runAsNonRoot: true和readOnlyRootFilesystem: true - 限制
sysctl和seccomp,禁用ptrace和mount系统调用 - Go 层白名单只是第一道过滤网,不是保险柜
最易被忽略的点:命令名本身也要校验。别以为只检查参数就够了——exec.Command(userInput, "-h") 这种写法,userInput 是攻击者可控的,直接变成任意程序执行。必须用白名单明确限定可执行的二进制名,比如只允许 tar、gzip、cp,其余一律拒绝。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










