
Python 开发者不该犯的 9 大安全错误
许多 Python 开发者都认为自己的代码是安全的,由于他们已经避免了那些显而易见的“菜鸟级”错误,列如不使用eval()函数,不将密码硬编码在代码里,以及使用 HTTPS 协议进行通信。不过,实际远比这复杂。那些最狡猾、最危险的安全漏洞,往往悄无声息地潜伏在代码的“边缘”——列如并发处理、数据反序列化、对象模型以及依赖链中。
作为一名资深开发者,我常常看到一些即使是经验丰富的同行也会犯的、很少被公开讨论的高级安全错误。这些错误就像隐藏在代码中的“定时炸弹”,一旦被恶意利用,后果不堪设想。今天,我们就来深入剖析这些鲜为人知的安全隐患,并提供切实可行的解决方案。
许多人习惯用一种看似简单直接的方式来创建临时文件,例如:
filename = "/tmp/data.txt"
with open(filename, "w") as f:
f.write("Sensitive info")
这种写法看起来很正常,但它却引入了一个被称为“TOCTOU”(Time-of-check to time-of-use,即“检查时刻到使用时刻”)的竞态条件漏洞。
它的工作原理是这样的:在你的代码检查filename路径是否存在和真正打开文件之间,存在一个微小的时间差。在这个时间窗口里,攻击者可以利用这个漏洞,将/tmp/data.txt文件链接到一个重大的系统文件,例如/etc/passwd,或者其他任何包含敏感信息的文件。当你的代码执行open()操作时,实际上是在对攻击者指定的那个文件进行写入,这可能导致数据泄露或系统文件被篡改。
如何解决?
Python 的tempfile模块正是为此类问题而生的。它能够确保创建的文件是安全的、唯一的,并且不存在竞态条件。
import tempfile
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b"Sensitive info")
print(f.name)
使用tempfile,你无需担心文件名冲突或被恶意链接,由于它会为你生成一个独一无二的、安全的文件名。
许多项目都依赖 YAML 配置文件来管理程序设置,一般会使用yaml.load()来解析这些文件。
import yaml
config = yaml.load(user_input, Loader=yaml.Loader) #
不过,上面这行代码是一个潜在的远程代码执行(RCE)漏洞。为什么?由于 PyYAML 的默认加载器(loader)不仅仅是解析 YAML 格式,它还可以构造任意的 Python 对象。如果用户输入是恶意的,它就能利用这个特性,在你的系统中执行任意代码。
一个著名的例子就是 Equifax 数据泄露事件,其核心问题就属于这种不安全的反序列化范畴。
如何解决?
修复这个问题超级简单,只需要将yaml.load()替换为yaml.safe_load()即可。
config = yaml.safe_load(user_input)
yaml.safe_load()只会加载基本的 YAML 类型,如字符串、数字和列表,从而避免了执行任意代码的风险。
你是否曾在日志中记录异常时,习惯性地使用repr(e)来获取异常的字符串表明?这看似无害,但你可能没有意识到,repr()方法可以调用由不受信任的对象所定义的__repr__方法。这意味着,如果一个用户控制的对象被传递给repr(),它就有可能在你的日志处理流程中触发任意代码执行。
class Evil:
def __repr__(self):
# 想象一下这里写入文件或调用os.system
raise Exception("Boom")
如何解决?
在将对象记录到日志之前,始终对其进行清理或转换为字符串。
logging.error("Error: %s", str(e))
使用str()函数可以安全地将对象转换为字符串,而不会触发其内部可能存在的恶意方法。
异步编程在 Python 中越来越流行,但它也引入了一些隐蔽的故障模式。如果你没有显式地处理asyncio.create_task中的异常,关键的错误信号可能会被悄无声息地丢失。攻击者超级喜爱利用这种“未被观察到的任务异常”来隐藏他们的恶意行为。
async def do_stuff():
raise RuntimeError("Security check failed")
asyncio.create_task(do_stuff()) # 异常被默默吞掉!
如何解决?
始终设置异常处理器。
task = asyncio.create_task(do_stuff())
task.add_done_callback(lambda t: t.exception())
或者,更好的做法是使用asyncio.gather(..., return_exceptions=False),这样任何一个任务失败都会立即抛出异常,让你能够及时发现并处理问题。
“原型污染”一般被认为是 JavaScript 独有的问题,但它在 Python 中同样存在。如果你不加过滤地将外部 JSON 数据合并到 Python 字典中,攻击者可以注入__class__或__subclasses__等保留键,从而导致权限提升。
payload = {"__class__": "os.system"}
mydict.update(payload) # 改变了内部状态
如何解决?
在合并字典之前,对键进行过滤。
for k, v in payload.items():
if k.startswith("__"):
raise ValueError("Reserved key injection attempt")
mydict[k] = v
通过这种方式,你可以阻止任何以双下划线开头的恶意键被注入,从而保护你的字典内部状态不被篡改。
许多 Python 开发者认为,只要在虚拟环境(venv)中工作,他们的代码就是安全的,由于这能“隔离”依赖。这是一个普遍存在的错误观念。虚拟环境并不提供执行沙箱。它们所做的仅仅是重定向导入路径。这意味着,如果你的代码加载了一个恶意的系统级库,虚拟环境根本无法保护你。
如何解决?
尽管 NIST 早在 2008 年就禁止了 MD5 用于加密目的,但在一些新的 Python 项目中,我依旧能看到开发者使用 MD5 或 SHA1 来进行密码哈希。这是一个已经过时且不安全的做法。这两种算法都存在已知的碰撞攻击,这意味着攻击者可以伪造一个具有一样哈希值的不同输入,从而绕过安全验证。
import hashlib
hashlib.md5(b"password").hexdigest()
如何解决?
使用Argon2或bcrypt等现代、安全的哈希算法。
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash("password123")
print(ph.verify(hash, "password123"))
这些算法专门为密码哈希设计,能够有效抵御碰撞和暴力破解攻击。
正则表达式拒绝服务(ReDoS)攻击是一个在 Python 中被低估的漏洞。一个编写不当的正则表达式在面对恶意输入时,可能会消耗大量的 CPU 资源,甚至导致整个进程挂起。
import re
pattern = re.compile(r"(a+)+$")
pattern.match("a" * 10**6 + "!")
上面这个正则表达式就是典型的例子,它会在匹配特定字符串时陷入“回溯”困境,导致程序长时间无响应。
如何解决?
import re2 as re
pattern = re.compile(r"(a+)+$")
print(pattern.match("aaaa!")) # 安全
假设你正在使用内部私有仓库来管理公司自己的 Python 包,并运行pip install yourlib来安装一个名为yourlib的包。你可能想当然地认为pip会从你的 Artifactory 仓库中获取它。但是,如果有人在公共的 PyPI 上上传了一个同名的恶意包,pip可能会优先从公共 PyPI 拉取那个恶意的包,而不是你期望的内部版本。这就是所谓的“依赖混淆攻击”。
如何解决?
pip install yourlib --index-url=https://internal.repo/simple
这些高级的 Python 安全问题,往往隐藏在那些看似“无害”的代码片段中。它们之所以难以被发现,是由于它们并非简单的语法错误,而是与程序的执行流程、库的底层机制以及生态系统本身的设计缺陷紧密相关。
安全开发不仅仅是避免已知的大坑,更是要深入理解你所使用的工具和库的底层工作原理。 它要求开发者具备一种“安全思维”,在编写每一行代码时,都思考到潜在的滥用场景。
希望这篇深度剖析的文章能协助你重新审视自己的 Python 代码,及时发现并修复那些可能存在的“定时炸弹”,从而构建出真正健壮、可靠和安全的应用程序。
Python编程 从入门到实践 第3版
¥69.8
购买
<script src="//mp.toutiao.com/mp/agw/mass_profit/pc_product_promotions_js?item_id=7546136993353318912"></script>
¥19.90
正版包邮 计算机和机器人(平)/什么是什么(学生版)(第1辑 皮特·克劳森 书店 计算机参考工具书书籍 畅想畅销书
¥38.80
人机大战 与机器对话:人类活着的意义是什么,探讨图灵测试提出的哲学、生物学和道德问题9787571009106湖南科技出版社全新正版
¥22.50
正版 德国少年儿童百科知识全书 什么是什么珍藏版 神秘机器人人工智能和好帮手 7-10岁少年儿童认知小百科小学生课外书
¥42.50
包邮正版 人机大战 与机器对话 人类活着的意义是什么 探讨图灵测试提出的哲学 生物学和道德问题 湖南科学技术出版社
¥35.00
人机大战 与机器对话 人类活着的意义是什么 探讨图灵测试提出的哲学 生物学和道德问题
¥34.52
人机大战 与机器对话 人类活着的意义是什么 探讨图灵测试提出的哲学 生物学和道德问题