Python 调用系统命令时的安全风险

3次阅读

subprocess.run() 应传入参数列表而非拼接字符串以避免shell注入;必须用shell时需用shlex.quote()转义变量,禁用os.system()等高危函数,并严格管控path、cwd等环境因素。

Python 调用系统命令时的安全风险

subprocess.run() 里直接拼接字符串会出事

subprocess.run() 执行系统命令时,如果把用户输入或变量直接用 +f-String 拼进命令字符串,就等于给 shell 开了后门。比如 f"ls {user_input}",用户输 ; rm -rf /,命令就变成 ls ; rm -rf / —— shell 会顺序执行。

真正安全的做法是让 subprocess 绕过 shell,把命令和参数拆成列表传进去:

subprocess.run(["ls", "-l", user_input])

这样 user_input 只会被当做一个参数值,不会触发分号、管道、重定向等 shell 特性。

  • 只要没显式传 shell=True,就默认不走 shell 解析,这是最有效的防护
  • 如果必须用 shell 功能(比如管道 |、通配符 *),那就得自己清理输入:用 shlex.quote() 包裹每个外部变量,再拼进字符串
  • shell=True + 字符串拼接 = 高危组合,生产环境应禁止

os.system() 和 os.popen() 为什么更危险

os.system()os.popen() 内部强制走 shell,且不提供参数分离接口。哪怕你只传一个变量,它也会被丢进 /bin/sh -c 执行,完全无法规避注入。

立即学习Python免费学习笔记(深入)”;

常见错误场景:日志归档脚本里写 os.system(f"tar -czf backup.tgz {target_dir}"),一旦 target_dir/tmp; cat /etc/passwd,结果就是打包完还顺手把密码文件吐到终端。

  • 这两个函数在 python 3.12 已被标记为 Deprecated,新代码别碰
  • 它们返回的是 shell 退出码,不是子进程对象,没法捕获 stdout/stderr,调试困难
  • subprocess.run() 的基础安全机制都绕过去了,纯属历史包袱

需要 shell 功能时怎么保命

真要依赖 shell 特性(比如 grep | awk 管道、$(date) 命令替换、~ 展开),就不能躲,但得控制风险范围。

核心原则:只让 shell 处理固定模板,所有动态部分提前转义,且限定执行环境:

import shlex cmd = f"ls -l {shlex.quote(user_path)} | head -n 5" subprocess.run(cmd, shell=True, check=True)
  • shlex.quote() 是唯一靠谱的字符串转义工具,它能处理空格、单双引号、反斜杠等各种边界情况
  • 避免用 os.path.expanduser("~") 这类函数拼路径后再进 shell,改用 pathlib.Path.home() 获取绝对路径,直接走参数列表方式
  • 如果命令逻辑复杂,优先拆成多个 subprocess.run() 调用,用 Python 处理中间数据,而不是全塞给 shell

PATH 和当前工作目录也是攻击面

很多人只盯着命令参数,忘了 PATH 环境变量和 cwd 同样能被利用。比如设置 env={"PATH": "/tmp:/usr/bin"},再执行 ls,实际跑的可能是 /tmp/ls —— 一个恶意二进制。

同理,如果 cwd 是用户可控目录,而命令里又用了相对路径(如 ./script.sh),就可能执行到非预期文件。

  • 显式指定 env 参数时,务必基于 os.environ.copy() 修改,不要凭空构造,否则可能丢失关键变量(如 LANG)导致编码异常
  • 涉及 cwd 时,先用 pathlib.Path.resolve() 规范化路径,再检查是否在白名单目录内(比如 if not target_path.is_relative_to(allowed_root): raise ValueError
  • 敏感操作建议加 timeoutlimit memory(通过 Resource.setrlimit()),防住 fork bomb 或无限循环

安全不是靠某一行代码,而是每层调用都得问一句:这个字符串最终会不会被当成代码执行?这个路径最终会不会被当成可执行目标?这种警惕感比记住所有 API 更重要。

text=ZqhQzanResources