beego框架本身不提供shell命令执行能力,所有os/exec调用均由开发者显式编写且无默认防护;直接拼接用户输入到exec.command参数或sh -c字符串中极易引发命令注入漏洞。

Beego 框架本身不提供 Shell 命令执行能力,所有调用 os/exec 或类似系统命令的操作,都必须由开发者显式编写,且默认不具备任何安全防护机制。直接拼接用户输入 + exec.Command 是高危行为,极易触发命令注入。
exec.Command 中参数拼接导致命令注入
常见错误是把用户传入的路径、文件名、选项等未经清洗就塞进 exec.Command 的参数列表:
// ❌ 危险:userInput 可能是 "; rm -rf /" 或 "$(rm -rf /)"
cmd := exec.Command("ls", "-l", userInput)
exec.Command 的第二个及以后参数会被当作独立参数传给底层进程(不经过 shell 解析),看似安全,但前提是这些参数本身不被 shell 环境间接调用。一旦你误用 sh -c 或 bash -c,风险立刻放大:
// ❌ 极度危险:userInput 完全可控 shell 上下文
cmd := exec.Command("sh", "-c", "ls -l "+userInput)
此时 userInput 会参与 shell 解析,;、&、$()、反引号等全部生效。
-
正确做法:永远避免
sh -c+ 字符串拼接 -
替代方案:拆解为明确参数列表,用
exec.Command(name, args...)直接调用二进制 - 若必须动态构造命令逻辑,改用 Go 原生 API(如
os.Stat、ioutil.ReadDir)代替ls、cat等
Beego Controller 中调用 Shell 的典型风险点
Beego 的 Controller 处理 HTTP 请求时,常因“快速实现”而引入 os/exec,比如:
- 提供“查看日志文件内容”接口,接收
filename参数后执行cat /var/log/app/{filename} - 实现“重启某服务”按钮,后台执行
systemctl restart myapp - 导出数据时调用
mysqldump并重定向输出
这些场景中容易忽略:
-
filename是否校验了路径遍历(如../../../etc/passwd) -
systemctl是否以非 root 用户运行(否则等于开放服务器控制权) -
mysqldump的数据库凭据是否硬编码在命令里(可能泄露到ps aux)
建议:
- 使用白名单限制可操作的文件名或服务名(如只允许
app.log、access.log) - 用
filepath.Join+filepath.Clean规范并截断路径,再检查是否仍在允许目录内 - 所有需要提权的操作,改用预设的 systemd socket 或本地 Unix domain socket 通信,而非直接 fork 进程
- 敏感命令的凭据绝不拼进命令行,改用环境变量(
cmd.Env)或配置文件(权限设为0600)
bee run 和开发期 Shell 调用的隐蔽风险
bee 工具本身会在某些场景下触发 Shell 调用,例如:
-
bee pack执行tar打包时若项目名含空格或特殊字符,未加引号可能导致截断 - 自定义
bee.json中的scripts字段(如"build": "go build && chmod +x ./myapp")若引用用户可控变量,也可能注入
这些虽不直接影响线上服务,但会污染本地开发环境或 CI 流程。实际项目中应:
- 避免在
bee.json的 scripts 里拼接变量 - CI 脚本中所有
go run、go build路径用双引号包裹 - 不在
conf/app.conf中存储可执行路径或命令模板(防止被模板引擎误解析)
真正难防的不是“会不会执行 Shell”,而是“有没有意识到哪一行正在越界”。Beego 的简洁性容易让人忽略底层仍是 Go —— 而 Go 的 os/exec 和 syscall 没有魔法,它和 C 的 system() 一样,输入即权力。











