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

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,结果就是打包完还顺手把密码文件吐到终端。
JTBC CMS(5.0) 是一款基于PHP和MySQL的内容管理系统原生全栈开发框架,开源协议为AGPLv3,没有任何附加条款。系统可以通过命令行一键安装,源码方面不基于任何第三方框架,不使用任何脚手架,仅依赖一些常见的第三方类库如图表组件等,您只需要了解最基本的前端知识就能很敏捷的进行二次开发,同时我们对于常见的前端功能做了Web Component方式的封装,即便是您仅了解HTML/CSS也
- 这两个函数在 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) - 敏感操作建议加
timeout和limit memory(通过resource.setrlimit()),防住 fork bomb 或无限循环
安全不是靠某一行代码,而是每层调用都得问一句:这个字符串最终会不会被当成代码执行?这个路径最终会不会被当成可执行目标?这种警惕感比记住所有 API 更重要。









