我在某处找到类似的代码:
USER_CONTROLLED = 'a'
open("settings.py", "w").write("USER_CONTROLLED = %s" % eval(repr(a)))
并在另一个文件中:
import settings
x = settings.USER_CONTROLLED * [0]
这是一个安全漏洞吗?
最佳答案
与您在IRC上看到的相反,肯定有一个x
会使eval(repr(x))
变得危险,因此像这样毫无限制地讲也是错误的。
想象一个自定义对象,它以不同的方式实现__repr__
。该文档在__repr__
上说,“它看起来应该像一个有效的Python表达式,可以用来重新创建具有相同值的对象”。但是,根本没有任何东西可以执行该准则。
因此,我们可以创建一个具有自定义__repr__
的类,该类返回一个字符串,该字符串在被评估时将运行任意代码。例如:
class MyObj:
def __repr__ (self):
return "__import__('urllib.request').request.urlopen('http://example.com').read()"
在该类型的对象上调用
repr()
表示它返回了可以确定地求值的字符串:>>> repr(MyObj())
"__import__('urllib.request').request.urlopen('http://example.com').read()"
在这里,这仅涉及向example.com发出请求。但是正如您所看到的,我们可以在此处导入任意模块并使用它们运行代码。该代码可能有任何副作用。因此绝对危险。
但是,如果我们将
x
限制为已知类型,并且知道对它们进行调用的方式,那么我们确实可以说出何时无法使用它运行任意代码。例如,如果repr()
是字符串,则x
的实现可确保所有内容均已正确转义,并且评估该对象的unicode_repr
将始终返回正确的字符串(甚至等于repr()
)。没有任何副作用。因此,我们应该在评估类型之前检查其类型:
if type(a) is not str:
raise Exception('Only strings are allowed!')
something = eval(repr(a))
请注意,我们此处不使用
x
进行继承感知类型检查。因为我绝对可以使上述的isinstance
从MyObj
继承:>>> x = MyObj()
>>> isinstance(x, str)
True
>>> type(x)
<class '__main__.MyObj'>
因此,您实际上应该在此处针对具体类型进行测试。
请注意,对于字符串,实际上没有理由调用
str
,因为如上所述,这将导致eval(repr(x))
本身。因此,您可以直接分配x
。但是,就您的实际用例而言,这里确实存在很大的安全性问题。您想要创建一个变量赋值并将该代码存储在Python文件中,以便稍后由实际的Python解释器运行。因此,您应该绝对确保分配的右侧不是任意代码,而是实际上是字符串的代表:
>>> a = 'runMaliciousCode()'
>>> "USER_CONTROLLED = %s" % eval(repr(a))
'USER_CONTROLLED = runMaliciousCode()'
>>> "USER_CONTROLLED = %s" % repr(a)
"USER_CONTROLLED = 'runMaliciousCode()'"
如您所见,评估
x
会将实际内容放在分配的右侧(因为它等效于repr()
)。但这会导致导入该文件时运行恶意代码。因此,您实际上应该只在其中插入字符串的repr,而完全忘记完全使用"…" % a
了。关于python - 是否有一个字符串s使得eval(repr(s))导致任意代码执行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33858507/