Python安全性:未收集的变量超出范围的危险

Python安全性:未收集的变量超出范围的危险,python,security,exploit,garbage,Python,Security,Exploit,Garbage,我在一个类中有一个解密变量并返回它的方法。使用后,我删除返回的带有“del”的变量 这些垃圾值被访问的危险是什么?我如何才能最好地保护自己不受它们的影响 代码如下: import decrypter import gc # mangled variable names used def decrypt(__var): __cleartext = decrypter.removeencryption(__var) return __cleartext __p_var = "&l

我在一个类中有一个解密变量并返回它的方法。使用后,我删除返回的带有“del”的变量

这些垃圾值被访问的危险是什么?我如何才能最好地保护自己不受它们的影响

代码如下:

import decrypter
import gc

# mangled variable names used
def decrypt(__var):
    __cleartext = decrypter.removeencryption(__var)
    return __cleartext

__p_var = "<512 encrypted password text>"
__p_cleartext = decrypt(__p_var)
<....do login with __p_cleartext...>
del  __p_var, __p_cleartext
gc.collect()

这会留下什么样的内存占用空间,可以窥探?

如果不存在对该值的其他引用,您的gc.collect通常会销毁该对象

然而,像字符串插入或缓存这样简单的操作可能会保留一个意外的引用,使该值在内存中保持活动状态。Python有许多实现(PyPy、Jython、PyPy),它们在内部做不同的事情。语言本身很少保证值是否或何时从内存中被删除

在您的示例中,还使用名称mangling。因为这种变形很容易手工复制,所以根本不会增加任何安全性


还有一个想法:不清楚您的安全模型是什么。如果攻击者可以调用您的decrypt函数并在同一进程中运行任意代码,那么有什么可以阻止他们包装decrypt以保留输入和输出的代码。

即使您调用gc.collect并释放这些字符串,它们仍可能保留在内存中。此外,字符串是不可变的,这意味着您没有(标准)方法覆盖它们。还请注意,如果您对这些字符串执行了操作,则可能会有一些字符串的副本

因此,如果可能的话,不要使用字符串


您需要覆盖内存(即使这样,内存也可能被转储到某个地方,比如页面文件)。使用字节数组并在完成后覆盖内存。

任何安全系统的强度都取决于其最薄弱的环节

很难判断当前系统中最薄弱的环节是什么,因为您还没有给出关于总体架构的任何详细信息,但是如果您实际使用的Python代码与您在问题中发布的类似(我们称之为
myscript.py

…然后,无论您如何或在何处解密密码,任何用户都可以出现并运行这样的脚本

import MySQLdb

def my_connect(*args, **kwargs):
    print args, kwargs
    return MySQLdb.real_connect(*args, **kwargs)

MySQLdb.real_connect = MySQLdb.connect
MySQLdb.connect = my_connect
execfile('/path/to/myscript.py')
…它将打印出明文密码,因此在C中实现解密就像在前门上安装十个门闩,但让窗户敞开

如果您想要一个关于如何保护您的系统的好答案,您必须提供关于总体架构的更多信息,以及您试图防止的攻击向量

如果有人设法破解root,你就完蛋了,但是有更好的方法可以对非root用户隐藏密码

但是,如果您确信运行此代码的机器是安全的(从某种意义上说,任何“未经授权”的用户都无法访问它),那么这些密码混淆的东西就没有必要了——您也可以直接将明文密码放入Python源代码中


更新

关于体系结构,我的意思是,您正在运行多少个独立的服务器,它们有什么职责,它们之间如何通信,以及/或如何与外部世界通信

假设主要目标是防止未经授权访问MySQL服务器,并且假设MySQL运行在与Python脚本不同的服务器上,那么为什么您更担心有人访问运行Python脚本的服务器并获取MySQL服务器的密码,而不是直接访问MySQL服务器

如果您使用“salt”作为加密MySQL密码的解密密钥,那么授权用户如何将该值传递给系统?他们是否必须通过ssh登录到服务器,并从命令行运行脚本,或者通过Web服务器访问脚本


无论哪种方式,如果有人破坏了运行Python脚本的系统,他们只需等待下一个授权用户出现,并且“嗅探”他们输入的“salt”。

值得注意的是,删除名称并不保证对象会被删除——它的其他名称可能仍然存在。如果您担心的是未经授权访问明文密码,可能有很多方法可以在它被假定为gc后访问它。如果你特别偏执,你可能想在C中完成这一部分,然后用随机数据覆盖RAM。关于你的编辑:即使你在C中实现了整个过程,并在完成时将进程地址空间归零,无法保证操作系统没有调出包含明文密码的地址空间部分,因此它可能会在磁盘上保留很长时间。即使您禁用了虚拟内存,如果有人可以访问进程的内存空间,也总会有机会获取明文密码。TBH,我不会担心的-如果密码只是针对MySQL的,那么有更简单的方法可以绕过它的安全性。有什么原因让你特别担心密码的安全性吗?随机的人有可能访问运行此Python代码的系统吗?@aya:我最担心的是;如果用户获得了对系统的访问权(甚至是根用户),那么文件中就没有任何东西可以帮助他们。我甚至不认为您使用的MySQLdb漏洞会起作用,因为脚本在没有salt的情况下无法传递登录凭据…在脚本启动时必须传递salt。但是,盐将存在于内存中的一个变量中。据我所知,获取此变量的唯一方法是嗅出或转储内存(或检查交换文件…但是在C中使用memlock应该可以防止它被交换)。所以,这就是我试图保护的。解密实际上使用了另一个运行进程中保存的salt密钥。此过程在用户登录并启动salt ha时启动
mlock() /*to keep the code memory resident (no swap)*/

char encrypt(data, salt){ 
    (...) 
    return encrypted_data
}

char decrypt(data, salt){ 
    (...) 
    return decrypted_data
}

stream_callback(stream_data){
    return decrypt(stream_data, decrypt(s-gdhen, jhgtdyuwj))
}

void main{ 
    char jhgtdyuwj=rand();
    s-gdhen = encrypt(<raw_user_input>, jhgtdyuwj);
}
#!/usr/bin/python
encrypted_username = 'feh9876\xhu378\x&457(oy\x'
encrypted_password = 'dee\x\xhuie\xhjfirihy\x^\xhjfkekl'
# MySQLdb.connect(host, username, password, database)
db = MySQLdb.connect(self.mysql_host,
                     c-obj.stream_callabck(encrypted_username),
                     c-obj.stream_callback(encrypted_password),
                     self.mysql_database)
#!/usr/bin/python
encrypted_username = 'feh9876\xhu378\x&457(oy\x'
encrypted_password = 'dee\x\xhuie\xhjfirihy\x^\xhjfkekl'
# MySQLdb.connect(host, username, password, database)
db = MySQLdb.connect(self.mysql_host,
                     c-obj.stream_callabck(encrypted_username),
                     c-obj.stream_callback(encrypted_password),
                     self.mysql_database)
import MySQLdb

def my_connect(*args, **kwargs):
    print args, kwargs
    return MySQLdb.real_connect(*args, **kwargs)

MySQLdb.real_connect = MySQLdb.connect
MySQLdb.connect = my_connect
execfile('/path/to/myscript.py')