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

时间:2013-05-27 16:31:05

标签: python security exploit garbage

我在类中有一个解密变量的方法,然后返回它。我在使用后用“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()

此时是否可以利用任何变量,包括__var和__cleartext?

谢谢!


我做了一些谷歌搜索。在我花了几个小时走错路之前......我听到的是:

  1. 将密码存储为系统上的盐渍哈希(现在正在执行)。
  2. 用户应在套件开始(现在完成)
  3. 输入哈希值
  4. 但是,盐应该保持在C过程而不是python。
  5. python脚本应该将哈希传递给C进程进行解密。
  6. python脚本正在处理mysql数据库的登录,并且需要密码才能打开数据库连接。

    如果代码符合......

    # MySQLdb.connect(host, user, password, database)
    mysql_host = 'localhost'
    mysql_db = 'myFunDatabase'
    hashed_user = '\xghjd\xhjiw\xhjiw\x783\xjkgd6\xcdw8'
    hashed_password = 'ghjkde\xhu78\x8y9tyk\x89g\x5de56x\xhyu8'
    db = MySQLdb.connect(mysql_host, <call_c(hashed_user)>, <call_c(hashed_password)>, mysql_db])  
    

    这会解决(至少)python全面留下垃圾的问题吗?


    P.S。我还发现了关于memset(Mark data as sensitive in python)的帖子,但我假设我使用C来解密哈希,这没有用。

    P.P.S。 dycrypter当前是一个python脚本。如果我要将memset添加到脚本然后使用py2exe或pyinstaller“编译”它...这实际上会做什么来帮助保护密码?我的直觉说不,因为所有pyinstaller都会打包正常的解释器和本地解释器创建的相同字节码......但是我不知道它有多少......?


    所以...根据Aya关于在C中制作加密模块的建议,以下设置会留下多少可辨别的内存占用。部分重大问题是;解密密码的能力必须在整个程序运行期间保持可用,因为它将被重复调用......这不是一次性的事情。

    创建一个在用户登录时启动的C对象。它包含解密例程,并保存用户在登录时输入的salt副本。通过使用随机生成的盐对其自己的加密例程进行哈希处理,运行对象(在内存中)中存储的盐被遮挡。

    随机生成的盐仍然必须保存在对象的变量中。这并不是为了确保盐的安全,而只是为了尝试混淆内存占用,如果有人应该看一眼(使盐很难识别)。 即 c-obj

    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);
    }
    

    然后,python脚本直接调用C对象,它将未加密的结果传递给MySQLdb调用,而不在任何变量中存储任何返回。即。

    #!/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)
    

    这可能会留下什么样的内存占用?

3 个答案:

答案 0 :(得分:3)

如果不存在对该值的其他引用,则 gc.collect 通常会破坏该对象。

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

在您的示例中,您还使用名称修改。由于手工修复很容易复制,因此根本不会增加任何安全性。

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

答案 1 :(得分:3)

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

所以尽可能不要使用字符串。

您需要覆盖内存(即便如此,内存可能会被转储到某个位置,例如转储到页面文件中)。完成后,使用字节数组并覆盖内存。

答案 2 :(得分:2)

任何安全系统都只能与其最薄弱的环节一样强大。

很难说出你当前系统中最薄弱的环节是什么,因为你还没有真正给出整体架构的任何细节,但如果你实际上使用的是你在问题中发布的Python代码(让我们称之为myscript.py)...

#!/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')

...将打印出明文密码,因此在C中执行解密就像在前门放置十个死锁一样,但是让窗户大开。

如果您想要获得有关如何保护系统的良好答案,则必须提供有关整体架构的更多信息,以及您试图阻止的攻击媒介。

如果有人设法破解root,你就会搞砸,但更好的方法是隐藏非root用户的密码。

但是,如果您对运行此代码的计算机是安全的感到满意(在任何“未授权”用户都无法访问的情况下),那么这些密码混淆的东西都不是必需的 - 您也可以直接将明文密码放入Python源代码中。


<强>更新

关于体系结构,我的意思是,您运行了多少个单独的服务器,它们有什么职责,以及它们如何相互通信和/或与外部世界进行通信?

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

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

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