使用 PHP file_get_contents()
请求 HTTPS 资源,发生以下错误:
Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in demo.php on line 79
错误显示:证书验证失败。证书验证发生在网络请求的哪个步骤?又为什么需要验证证书呢?一切要从 HTTPS 说起。
HTTPS 通信流程
HTTPS = HTTP SSL,是在 HTTP 的基础上加上了 SSL 保护壳,信息的加密过程就是在 SSL 中完成的。它的通信流程如下:
HTTPS 通信流程
为什么需要验证证书?
HTTPS 通信流程中有一步「验证证书有效性」,证书是由受信任的 CA 机构颁布的,验证证书有效性可以确保你在和可信任的服务进行通信,避免第三方伪装的站点骗取你的信息。
如何验证证书?
首先,一个数字证书通常包含了以下信息:
- 公钥
- 持有者信息
- 证书认证机构(CA)的信息
- CA 对这份文件的数字签名及使用的算法
- 证书有效期
- ……
CA 在签发证书时,会将上述所有信息进行 Hash 计算,得到一个 Hash 值,我们在这称之为 Hash1。然后 CA 使用自己的私钥对该 Hash 值加密,生成 Certificate Signature(签名),将 Certificate Signature 添加到证书上,形成数字证书。
客户端在验证证书时,会使用同样的算法对证书信息进行 Hash 计算,得到 Hash2,并使用 CA 的公钥解出 Hash1。如果对比 Hash2 与 Hash1 相同,则证书可信,反之证书不可信。
CA 证书签名、校验过程
通常来说,浏览器和操作系统中集成了 CA 公钥信息。而我们此次 PHP 报错的原因,正是因为我们缺少了这个信息。
问题解决方法
下载 .pem 文件:https://curl.haxx.se/ca/cacert.pem
这个文件的内容就是「Bundle of CA Root Certificates」,即「根证书 CA 公钥集合」。
修改 php.ini,补充 cacert.pem 文件具体路径:
代码语言:javascript复制extension=php_openssl.dll
openssl.cafile=/home/disk1/file/cacert.pem
重启 PHP 服务后,问题得到解决。
绕过验证与绕过验证的风险
file_get_contents()
允许你通过传入 PHP SSL 上下文绕过 HTTPS 验证:
$arrContextOptions=array(
"ssl" => array(
"verify_peer" => false,
"verify_peer_name" => false,
),
);
$content = file_get_contents($value['art_url'], false, stream_context_create($arrContextOptions));
但不建议在生产环境这么做,毕竟存在安全问题。
参考资料
- 问题解决方法
- file_get_contents SSL operation failed with code 1 SSL3_GET_SERVER_CERTIFICATE certificate verify failed[1]
- PHP 配置文件修改[2]
- `file_get_contents()` 函数介绍[3]
- 浏览器如何验证HTTPS证书的合法性?[4]
- What exactly is cacert.pem for?[5]
参考资料
[1]file_get_contents SSL operation failed with code 1 SSL3_GET_SERVER_CERTIFICATE certificate verify failed: http://www.bigsoft.co.uk/blog/2017/04/29/file-get-contents-ssl-operation-failed-with-code-1-ssl3-get-server-certificate-certificate-verify-failed
[2]PHP 配置文件修改: https://stackoverflow.com/questions/55526568/failed-loading-cafile-stream-in-file-get-contents
[3]file_get_contents()
函数介绍: https://www.php.net/manual/zh/function.file-get-contents.php
[4]浏览器如何验证HTTPS证书的合法性?: https://www.zhihu.com/question/37370216
[5]What exactly is cacert.pem for?: https://stackoverflow.com/questions/14987857/what-exactly-is-cacert-pem-for