CodeWalk

subprocess shell注入防范与安全调用

作者:孤独的心 · 2026-05-30 12:55

请解释Python subprocess模块中Shell注入攻击的原理和防范方法。为什么不应该使用shell=True处理用户输入?正确的做法是什么?对比os.system()subprocess的安全差异。

回答

孤独的心

Shell注入攻击原理

import subprocess

# 危险!用户输入注入
user_input = '; rm -rf /'
subprocess.run(f'echo {user_input}', shell=True)
# 实际执行:echo ; rm -rf /

shell=True时,字符串传给系统的shell(/bin/sh -c),shell会解析;|$()等特殊字符。

为什么不建议os.system()

import os
os.system(f'ping {user_ip}')  # 同样会Shell注入
# 比subprocess更容易出错
  • os.system()总是使用shell
  • 无法捕获输出
  • 不可控制进程(超时、管道等)

正确的安全做法

方案1:列表形式(推荐)

# 安全!不需要shell解析
subprocess.run(['echo', user_input], shell=False)
# 即使user_input='hello; rm -rf /', 也会作为echo的参数输出

args为列表时,直接execv系统调用,绕过shell。

方案2:需要shell特性时手动转义

import shlex

# 如果确实需要shell=True(管道、通配符等)
user_input = shlex.quote(user_input)  # 转义特殊字符
subprocess.run(f'grep {user_input} /var/log/syslog', shell=True)

方案3:shlex.split解析用户输入

import shlex
# 将用户输入的字符串安全解析为参数列表
args = shlex.split(user_input)
subprocess.run(args)

安全策略总结

def safe_run(user_input: str):
    # 白名单验证
    ALLOWED_COMMANDS = {'ls', 'cat', 'echo'}
    parts = shlex.split(user_input)
    if parts[0] not in ALLOWED_COMMANDS:
        raise ValueError('命令不允许')
    subprocess.run(parts)  # 列表形式,始终安全

黄金法则: 永远不要用shell=True处理任何形式的用户输入。如果必须用,先通过shlex.quote()转义。