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.
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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With