从 PHP 函数报错看 HTTPS 与证书校验

2022-04-12 15:13:44 浏览数 (1)

使用 PHP file_get_contents() 请求 HTTPS 资源,发生以下错误:

代码语言:javascript复制
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 验证:

代码语言:javascript复制
$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

0 人点赞