【问题标题】:PHP stream_socket_client fails to connectPHP stream_socket_client 连接失败
【发布时间】:2015-10-13 20:17:42
【问题描述】:

我正在尝试通过 php 的 stream_socket_client() 命令联系一个 url,该命令失败,没有错误代码或 msg。这是否使用 openssl、curl 或其他东西?该站点使用 https 并且可以在 Web 浏览器中正常工作。

当我直接调用 openssl 时,它会连接,但对我的证书链似乎不满意。

root@drupal7 drupal7/includes# openssl s_client -connect test.cgps.org:443
CONNECTED(00000003)
Verify return code: 21 (unable to verify the first certificate)

与卷曲相同

curl https://test.cgps.org/?q=/admin/config/search/clean-urls/check
curl: (60) SSL certificate problem: unable to get local issuer certificate

所以我认为我需要将中间证书安装到 Linux 中,我通过将 Apache 使用的相同中间证书放入 /usr/local/share/ca-certificates 并运行 update-ca-certificates 来做到这一点。

root@drupal7 drupal7/includes# update-ca-certificates
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.

仍然没有运气......

【问题讨论】:

  • "这个是用 openssl、curl 还是别的什么?" ——完全不同的东西,不仅仅是一点点不同;非常不一样。你在问,“这种食物是苹果、梨还是奶酪?”当答案是,你正试图吃一根撬棍。您不使用套接字流来下载网页,而是使用它来建立双向 TCP 连接,如果您很聪明,您可以使用它来获取 Web 内容,就像您可以使用撬棍一样从树上拔苹果和梨,或者敲开奶酪轮。
  • 好的,但是 stream_socket_client 是否会因为同样的原因而失败,对证书不满意?有什么方法可以进一步解决 stream_socket_client 故障吗?
  • stream_socket_client() 在会话层运行,证书直到应用层才开始重要,所以不,绝对没有理由证明证书很重要。最有可能的是,Web 服务器等待对 TLS 握手的响应导致连接超时,这意味着它成功连接,成功接收数据包,并且连接在 5 秒后成功关闭,因此从这一点来看没有错误stream_socket_client() 的视图。
  • 而且,为了让这一点更明显......如果你想下载 HTTPS 内容,stream_socket_client() 对于你的目的来说太低了。在开始接近所需位置之前,您必须在套接字流之上编写自己的 TLS 客户端,加上自己的 HTTP 客户端。使用卷曲。找出证书或授权链出了什么问题。
  • @Ghedipunk stream_socket_client('tls://example.com:443', ...) 工作得很好。然后,您只需像未加密一样读取和写入数据。不需要你自己的 TLS 客户端?!

标签: php ssl curl


【解决方案1】:

尽管在 cmets 中声明说 PHP 的 steam_socket_client() 级别太低,无法关心证书,但我知道它确实在乎,因为我观察到它返回false 在标准位置缺少某些证书的系统上,提供流上下文(告诉它在哪里寻找证书)阻止这种情况发生。

令人沮丧的是,$errno 在您修复此问题之前可能保持为 0,因此您最好先使用其他工具检查您的证书颁发机构目录。

诊断

通过curl

要使用curl,您通常可以通过添加--verbose 标志来弄清楚发生了什么。这应该告诉您正在使用哪个连接的 PEM 证书文件 (CAfile/cacert) 和 CA 目录 (capath),如果有的话。在输出的顶部,你会得到类似的东西:

[...]
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: /home2/wynddorg/private_html/trusted_roots
[...]

一旦您知道这些默认设置是什么,您就可以调整您的系统配置或简单地添加--cacert <concatenated_cert_path> 标志或--capath <c_rehash'd_directory> 或两者兼而有之。

通过openssl s_client

使用s_client 有点棘手;根据您的版本和编译方式,您可能可以访问其-trace 选项,但请尝试:

strace -e open,stat openssl s_client -connect test.cgps.org:443 [-CApath <dir>]
# this trick provided by the amazing xemdetia in irc://freenode/%23%23openssl

CONNECTED(00000003) 消息之后大约 5 行,您应该会看到一个 open("...", O_RDONLY)(或多个),它应该显示哪些位置正在用于查找证书。


我缺少一个或多个证书。现在呢?

一旦您知道缺少哪些证书,您可以下载它们as shown here,然后执行任何操作:

  • 将它们连接成一个 PEM 文件(最好是in order),然后在您尝试连接到您的站点时指定该文件
  • 将它们作为单独的“.crt”文件保存在默认目录中,然后在该目录上运行c_rehash(可能需要root/sudo)
  • 将它们作为单独的“.crt”文件保存在另一个目录中,并在连接时将其明确指定为 capath。您可能希望在共享主机上执行此操作,或者如果您不希望所有应用程序都信任证书,而只信任某些证书。

一旦您使用 openssl s_clientcurl 或两者,您可以指定相同的选项,如下所示:

$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'cafile', <concatenated_cert_file_path>);
stream_context_set_option($context, 'ssl', 'capath', <c_rehashed_cert_dir_path>);
// You only really need to provide one of the two options above. Using capath is
// faster, but this only matters if you'll be making a huge number of
// connections.
$ssc = stream_socket_client(
    'ssl://subdomain.example.com:443',
    $errno,
    $errstr,
    30,
    STREAM_CLIENT_CONNECT,
    $context
);
if ($ssc) { /* ... */  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 2012-08-30
    • 2023-04-04
    • 2023-03-27
    • 2016-06-10
    • 2016-09-15
    相关资源
    最近更新 更多