【问题标题】:How can I tell if my memory has been released securely by a PHP application?如何判断我的内存是否已被 PHP 应用程序安全释放?
【发布时间】:2015-03-29 14:01:50
【问题描述】:

这可能更适合 security.stackexchange.com,但我特别对 PHP 感到好奇。

我正在一个应用程序中使用 openssl,我注意到 openssl 资源的免费操作。这很可能只是一般的内存释放,但考虑到加密性质,它可能会被视为一种特殊情况。

AFAIK 在应用程序空间内无法确保从内存中删除变量。然而,在 Zend 领域,C 扩展是清理已知的敏感数据,还是只是释放内存? openssl_pkey_free 是否安全释放内存?我如何才能断言它已安全发布以将其应用于我将来可能会好奇的其他扩展?

我不是安全分析师,所以我对安全的定义相当模糊。

【问题讨论】:

    标签: php security securezeromemory


    【解决方案1】:

    TL;DR:没有。

    在我看之前,我的回答是:由于 PHP 是一种动态语言,你应该假设它没有被清除,直到证明不是这样(例如,使用 Volatility)。根据前 FreeBSD 安全官 Colin Percival 的说法,"Zeroing Buffers is Insufficient" -- 所以这甚至可能无关紧要。

    但这是一个非常无聊的答案。引擎盖下是什么?

    openssl_pkey_free() 有什么作用?

    openssl_pkey_free() 由 PHP 在 ext/openssl/openssl.c#545 中定义:

    void EVP_PKEY_free(EVP_PKEY *x)
    {
        int i;
    
        if (x == NULL)
            return;
    
        i = CRYPTO_add(&x->references, -1, CRYPTO_LOCK_EVP_PKEY);
    #ifdef REF_PRINT
        REF_PRINT("EVP_PKEY", x);
    #endif
        if (i > 0)
            return;
    #ifdef REF_CHECK
        if (i < 0) {
            fprintf(stderr, "EVP_PKEY_free, bad reference count\n");
            abort();
        }
    #endif
        EVP_PKEY_free_it(x);
        if (x->attributes)
            sk_X509_ATTRIBUTE_pop_free(x->attributes, X509_ATTRIBUTE_free);
        OPENSSL_free(x);
    }
    
    static void EVP_PKEY_free_it(EVP_PKEY *x)
    {
        if (x->ameth && x->ameth->pkey_free) {
            x->ameth->pkey_free(x);
            x->pkey.ptr = NULL;
        }
    #ifndef OPENSSL_NO_ENGINE
        if (x->engine) {
            ENGINE_finish(x->engine);
            x->engine = NULL;
        }
    #endif
    }
    

    如您所见,它调用了一个名为EVP_PKEY_free()的函数,该函数由/crypto/evp/p_lib.c#L376中的openssl定义:

    void EVP_PKEY_free(EVP_PKEY *x)
    {
        int i;
    
        if (x == NULL)
            return;
    
        i = CRYPTO_add(&x->references, -1, CRYPTO_LOCK_EVP_PKEY);
    #ifdef REF_PRINT
        REF_PRINT("EVP_PKEY", x);
    #endif
        if (i > 0)
            return;
    #ifdef REF_CHECK
        if (i < 0) {
            fprintf(stderr, "EVP_PKEY_free, bad reference count\n");
            abort();
        }
    #endif
        EVP_PKEY_free_it(x);
        if (x->attributes)
            sk_X509_ATTRIBUTE_pop_free(x->attributes, X509_ATTRIBUTE_free);
        OPENSSL_free(x);
    }
    

    它会进行一些健全性检查,然后调用OPENSSL_free(),这只是an alias for CRYPTO_free()

    最后,CRYPTO_free() 被定义为here

    void CRYPTO_free(void *str)
    {
        if (free_debug_func != NULL)
            free_debug_func(str, 0);
    #ifdef LEVITTE_DEBUG_MEM
        fprintf(stderr, "LEVITTE_DEBUG_MEM:         < 0x%p\n", str);
    #endif
        free_func(str);
        if (free_debug_func != NULL)
            free_debug_func(NULL, 1);
    }
    

    在典型情况下似乎只是调用free_func(),这是一个指向free() 的指针。在这些操作中,我没有看到任何将内存归零的尝试。

    但我真的想在 PHP 中将内存归零。我该怎么办?!

    如果您可以安装 PECL 扩展,libsodium 除了提供secure memory allocation utilities 之外,还提供\Sodium\memzero()

    请记住,将内存归零是一种缓解策略,可在发生妥协时使用。如果您的 PHP 代码可以从磁盘(或数据库)读取私钥,攻击者可能会重放代码并直接窃取密钥。防止这种情况发生的方法是将您的密钥存储在hardware security module 中,并且永远不要直接触摸它。

    【讨论】:

    • 我有点预料到会是这样,但不确定他们是否有一些聪明的方法来破坏释放的内存。我的应用程序在事件中收到一个随机密码,并从保险库请求相应的密钥(每个令牌一次使用)。所以我最大的问题是是否有人可以读取内存,但如果他们可以的话,我会遇到比我的签名密钥是否被泄露更大的问题。
    • 您可以遵循一些最佳实践来提供帮助。如果您担心序列化和/或堆栈跟踪,请将其存储在类常量而不是私有变量中。将您的加密操作与网络的其余部分(尤其是数据库)隔离在单独的机器上——这基本上是 HSM 所做的。
    猜你喜欢
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多