我如何判断我的内存是否已被 PHP 应用程序安全释放?
How can I tell if my memory has been released securely by a PHP application?
这可能更适合 security.stackexchange.com,但我特别想知道 PHP。
我在应用程序中使用 openssl,我注意到 openssl 资源的免费操作。这很可能只是一般的内存释放,但考虑到密码学性质,它可能会作为特殊情况处理。
应用程序内部的 AFAIK space 无法确保从内存中删除变量。然而,在 Zend land 中,C 扩展是清理已知的敏感数据,还是只是释放内存? openssl_pkey_free
是否安全释放内存?我如何断言它已安全发布,以便将其应用于我将来可能感兴趣的其他扩展?
我不是安全分析师,所以我对安全的定义比较模糊。
TL;DR: 没有。
在我看之前,我的答案是:由于 PHP 是一种动态语言,您应该 假设它没有被清除 直到证明不是这样(例如使用 Volatility)。根据前 FreeBSD 安全官 Colin Percival 的说法,"Zeroing Buffers is Insufficient" —— 所以这甚至可能无关紧要。
但这是一个非常无聊的答案。幕后是什么?
openssl_pkey_free() 有什么作用?
openssl_pkey_free()
由 ext/openssl/openssl.c#545 中的 PHP 定义:
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()
的函数,该函数由openssl在/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);
}
它会进行一些完整性检查,然后调用 OPENSSL_free()
,对于 CRYPTO_free()
.
,这只是 an alias
最后定义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 offers \Sodium\memzero()
in addition to secure memory allocation utilities。
请记住,当发生妥协时,清零内存是一种缓解策略。如果您的 PHP 代码可以从磁盘(或数据库)读取私钥,攻击者可能可以重放代码并直接窃取密钥。防止这种情况的方法是将您的密钥存储在 hardware security module 中并且永远不要直接触摸它。
这可能更适合 security.stackexchange.com,但我特别想知道 PHP。
我在应用程序中使用 openssl,我注意到 openssl 资源的免费操作。这很可能只是一般的内存释放,但考虑到密码学性质,它可能会作为特殊情况处理。
应用程序内部的 AFAIK space 无法确保从内存中删除变量。然而,在 Zend land 中,C 扩展是清理已知的敏感数据,还是只是释放内存? openssl_pkey_free
是否安全释放内存?我如何断言它已安全发布,以便将其应用于我将来可能感兴趣的其他扩展?
我不是安全分析师,所以我对安全的定义比较模糊。
TL;DR: 没有。
在我看之前,我的答案是:由于 PHP 是一种动态语言,您应该 假设它没有被清除 直到证明不是这样(例如使用 Volatility)。根据前 FreeBSD 安全官 Colin Percival 的说法,"Zeroing Buffers is Insufficient" —— 所以这甚至可能无关紧要。
但这是一个非常无聊的答案。幕后是什么?
openssl_pkey_free() 有什么作用?
openssl_pkey_free()
由 ext/openssl/openssl.c#545 中的 PHP 定义:
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()
的函数,该函数由openssl在/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);
}
它会进行一些完整性检查,然后调用 OPENSSL_free()
,对于 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 offers \Sodium\memzero()
in addition to secure memory allocation utilities。
请记住,当发生妥协时,清零内存是一种缓解策略。如果您的 PHP 代码可以从磁盘(或数据库)读取私钥,攻击者可能可以重放代码并直接窃取密钥。防止这种情况的方法是将您的密钥存储在 hardware security module 中并且永远不要直接触摸它。