php – openssl encypt / decrypt不一致的工作/失败
我在
PHP中使用openssl_ *方法看到一些奇怪的行为. 50%的时间,它会失败,抛出未知密码算法,另外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密码在系统级别列出了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,$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)返回一个false. 除了它是标准的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年,它指向的资源略微不同于我的想法,但足够接近让我有所反应:
如果HHVM openssl扩展没有建立这些相同的回调,可能会导致调用错误的回调. 我要执行的下一个测试是时间允许的,是在上述故障点中放置警报(以静态syslog调用的形式),以准确地确定哪个测试失败…如果我可以安装相同的设置就像你在虚拟机上一样,我可以重现同样的怪异行为. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |