6

This might be better suited for security.stackexchange.com, but I'm curious about PHP in particular.

I'm working with openssl in an application and I notice the free operations for the openssl resources. This could very well just be just a generic release of memory, but given the cryptographic nature it may be handled as a special case.

AFAIK inside the application space there is no way to ensure a variable is deleted from memory. However, in Zend land, do the C extensions clean up known sensitive data, or do they just release the memory? Does openssl_pkey_free release the memory securely? How can I go about making an assertion that it has been released securely to apply it to other extensions I may be curious about in the future?

I'm not a security analyst, so my definition of securely is rather vague.

4

1 回答 1

8

TL;DR: No.

Before I even look, my answer is: Since PHP is a dynamic language, you should assume it's not cleared until proven otherwise (e.g. with Volatility). According to former FreeBSD security officer Colin Percival, "Zeroing Buffers is Insufficient" -- so it might not even matter.

But that's an incredibly boring answer. What's under the hood?

What does openssl_pkey_free() do?

openssl_pkey_free() is defined by PHP in 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
}

As you can see, it calls a function called EVP_PKEY_free(), which is defined by openssl in /crypto/evp/p_lib.c#L376:

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

It does some sanity checks then calls OPENSSL_free(), which is just an alias for CRYPTO_free().

Finally, CRYPTO_free() is defined 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);
}

It seems to just call free_func() in the typical case, which is a pointer to free(). At no point in these operations did I see any attempt to zero out memory.

But I really want to zero memory in PHP. How can I?!

If you can install PECL extensions, libsodium offers \Sodium\memzero() in addition to secure memory allocation utilities.

Please remember that zeroing memory is a mitigation strategy for when a compromise happens. If your PHP code can read the private key from disk (or from a database), the attacker can probably replay the code and steal the key directly. The way to protect against this is store your keys in a hardware security module and never touch it directly.

于 2015-03-29T16:48:50.697 回答