Python 调用系统命令的安全注意事项

6次阅读

应使用 subprocess.run() 替代 os.system() 和 os.popen(),传入参数列表而非拼接字符串,禁用 shell=True,显式控制 env 和 cwd,并警惕 SUID 二进制与环境变量劫持风险。

Python 调用系统命令的安全注意事项

使用 subprocess.run() 替代 os.system()os.popen()

os.system()os.popen() 会直接将字符串交给 shell 解析,一旦参数含用户输入,极易触发命令注入。比如 os.system(f"ls {user_input}") 遇到 user_input = "; rm -rf /" 就会执行恶意命令。

正确做法是用 subprocess.run() 并传入参数列表(而非拼接字符串),让 python 绕过 shell,直接调用程序:

subprocess.run(["ls", "-l", user_input])  # 安全:user_input 被当作文本参数,不会被 shell 解析

关键点:

  • 始终用 list 形式传参,避免 shell=True
  • 若必须用 shell 功能(如管道、通配符),则需严格过滤或转义用户输入,不推荐
  • subprocess.run(..., capture_output=True, text=True)check_output() 更清晰可控

警惕 shell=True 的隐式风险

即使你没显式写 shell=True,某些封装函数(如旧版 commands.getoutput(),或第三方库的 exec 函数)内部可能默认启用 shell,导致参数被重新解析。

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

常见踩坑场景:

  • 调用 subprocess.run("ls *.py", shell=True) —— * 由 shell 展开,但若 *.py 来自用户,就等于放行任意文件名匹配逻辑
  • shlex.split() 处理用户输入再传给 subprocess,却仍设 shell=True,等于白做
  • windows 上误以为 shell=False 就绝对安全,其实 cmd.exe 的某些内置命令(如 dir)仍依赖 shell 行为

建议:除非明确需要 shell 特性(且已评估输入完全可信),否则一律禁用 shell=True

环境变量与工作目录的污染风险

子进程会继承父进程的 os.environ,其中可能含敏感信息(如 HOMEPATHssh_AUTH_SOCK),或被恶意篡改影响命令行为。

实操建议:

  • 显式传入干净的 env 参数,例如 env=dict(os.environ, PATH="/usr/bin:/bin")
  • cwd 明确指定工作目录,避免子进程在意外路径下读写文件
  • 对敏感命令(如涉及密钥操作的 gpgssh),考虑用 env={} 清空环境后按需注入必要变量

权限提升与进程逃逸的实际约束

Python 进程本身权限决定子进程上限:普通用户启动的脚本,无论怎么调用 subprocess,都无法直接执行 sudo 或写入 /etc——除非提前配置了免密 sudo 规则,而这属于系统层配置问题,不是 Python 能绕过的。

但仍有两类易被忽略的越权路径:

  • 子进程调用的二进制本身有 SUID 位(如 /usr/bin/passwd),此时它以文件所有者身份运行,可能提权
  • 通过 LD_PRELOADpythonpath 等环境变量劫持子进程加载的库或模块,间接控制行为

所以不要只盯着“命令怎么拼”,还得看“谁在跑、在哪跑、带了什么环境”。尤其在容器或共享服务器中,这些边界比语法更关键。

text=ZqhQzanResources