subprocess shell注入防范与安全调用
请解释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()转义。