gpt4 book ai didi

php - openssl 加密/解密工作不一致/失败

转载 作者:太空宇宙 更新时间:2023-11-03 16:43:11 26 4
gpt4 key购买 nike

我发现 PHP 中的 openssl_* 方法有一些奇怪的行为。 50% 的时间,它会失败,抛出 Unknown cipher algorithm,而另外 50% 的时间,它会正确编码我的数据。这是我的代码中的相关片段:

$iv = openssl_random_pseudo_bytes(16);
$hash = openssl_encrypt($raw, "AES-128-CBC", $hashing_secret, OPENSSL_RAW_DATA, $iv);
// send $iv.$hash

使用 openssl_get_cipher_methods 给我:

[0] => AES-128-CBC
...
[81] => aes-128-cbc

所以我知道密码可用。此外,$ openssl ciphers 将 AES-128-CBC 列为系统级别的可用密码(但是,有人告诉我 PHP 的捆绑 openssl 是独立的)

我正在运行 Ubuntu 14.04、php5.5.9-1ubuntu4.14、openssl 1.0.1f 2014 年 1 月 6 日(phpinfo 中列出的版本相同)。如果相关,所有这些代码都通过 nginx/php-fpm 在 Silex 框架下运行。

更新:更多信息...

我做了更多的测试。我写了一个小脚本,只循环 x 次,对一些数据进行编码。

set_error_handler(function() use (&$errorCount) {
$errorCount++;
});

for ($i = 0; $i < $numTests; $i++) {
$hash = openssl_encrypt($data, "AES-128-CBC", $hashing_secret, OPENSSL_RAW_DATA, $iv);
}

如果我在同一台服务器上运行它(通过 php test.php),它会始终如一地运行 - 即 $errorCount == 0 每次。这让我相信它是:a) silex 或 b) 阻碍功能的 fastcgi 进程 - 我已经添加了这些标签。

虽然现在还不确定从这里去哪里......

第二次更新

我做了更多的测试。我把测试脚本放在 nginx 后面,运行 php-fpm。这里奇怪的是,要么 a) 100% 的时间失败,要么 b) 失败 0 次,而不是两种结果都有一点点。这让我相信是 nginx 或 php-fpm 才是罪魁祸首。

最佳答案

这看起来可能是 OpenSSL 错误锁定错误。您应该确保同一进程空间中的任何时候都只使用一个 OpenSSL 对象。

要验证,请运行测试脚本,使其成为唯一使用 OpenSSL 的脚本。它仍然有 50% 的时间失败吗?还是仅在对脚本进行多个并发访问时才会发生故障?

如果它仍然发生,它几乎必须是 php-fpm 中的错误——它正在实例化函数并且在发生错误之前没有正确清除其数据区域。在那种情况下,我希望它每两次调用失败一次,而不是“平均 50%”,而是每一次偶数调用恰好失败一次。在那种情况下,我会尝试使用不同版本的 OpenSSL。

要锁定 openssl,您可以尝试使用 flock并实例化一个锁文件以供 SSL 函数使用(首先检查锁是否可用,然后运行该函数并解锁)。试试这个,看看它是否有效。如果是这样,您可以研究一种更有效的方法 - 例如,您可以使用 MySQL LOCK() 或 semaphore如果可用的话。

探险

5.5.9 中的异常函数可在 ext/openssl/openssl.c 中找到,抛出的错误是初步检查之一。还没有惊喜:

/* {{{ proto string openssl_encrypt(string data, string method, string password [, long options=0 [, string $iv='']])
Encrypts given data with given method and key, returns raw or base64 encoded string */
PHP_FUNCTION(openssl_encrypt)
{
long options = 0;
char *data, *method, *password, *iv = "";
int data_len, method_len, password_len, iv_len = 0, max_iv_len;
const EVP_CIPHER *cipher_type;
EVP_CIPHER_CTX cipher_ctx;
int i=0, outlen, keylen;
unsigned char *outbuf, *key;
zend_bool free_iv;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|ls", &data, &data_len, &method, &method_len, &password, &password_len, &options, &iv, &iv_len) == FAILURE) {
return;
}
cipher_type = EVP_get_cipherbyname(method);
if (!cipher_type) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
RETURN_FALSE;
}

所以我们可以假设 EVP_get_cipherbyname(method) 返回一个错误。

除了它是一个标准的 SSL 函数。我发现这很简洁(而且很可能已经过时)reply这似乎表明食谱中某处有一些棕榈汁。但这并不能解释为什么函数应该每两次失败一次。

函数是here on github .它初始化 OpenSSL,并通过 ancillary function 获取方法名称。这将返回一个指向非空内存的指针。

我有一个牵强的假设,该函数随机返回类似于 0 或 81 的东西(因为这两个字符串都在您的密码列表输出中,索引为 0 和 81)并且 0 等于 NULL,因此失败。看起来它不能那样工作,它也应该在 CLI 中这样做。 但为了确定,验证是否只有特定密码失败(而例如 AES-256-CBC 有效)。

另一种可能性是 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS, NULL) 调用失败了。如果这个测试(在 Ubuntu 上;其他平台表现不同)失败,就会发生这种情况:

int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
{
if (pthread_once(once, init) != 0)
return 0;

return 1;
}

这再次表明 libcrypto 内部存在一些共享资源冲突。

作为另一个测试,我建议你不要调用随机字节 IV 初始化并尝试使用固定 IV;那是因为我也偶然发现了 this note ,它指向的资源与我想象的略有不同,但足够接近让我使用react:

It appears that openssl_random_pseudo_bytes(), which calls openssl, causes the underlying libcrypto to invoke a callback that was established previously by PostgreSQL library as part of the lock portability callbacks for multi-threading of openssl.

Some information on the topic can be found here http://wiki.openssl.org/index.php/Manual:Threads(3)

如果 HHVM openssl 扩展没有建立这些相同的回调,它可能会导致调用错误的回调。

如果时间允许,我将要执行的下一个测试是在上述故障点中放置警报(以静态系统日志调用的形式),以准确查明哪个测试失败...只要我可以安装与您在 VM 上相同的设置,并且我可以重现相同的怪异行为。

关于php - openssl 加密/解密工作不一致/失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37146547/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com