本文目录导读:

- 开启详细的错误报告
- 使用 cURL 进行详细调试
- 使用 stream context 调试
- 使用 OpenSSL 命令行工具对比调试
- 使用 PHP OpenSSL 扩展直接调试
- 常见 SSL/TLS 问题及解决方法
- 最佳实践建议
在 PHP 中进行 SSL/TLS 调试,主要涉及连接 HTTPS 服务时的证书验证、密码套件选择、握手过程等问题,以下是系统的调试方法和工具:
开启详细的错误报告
<?php
// 开启所有错误显示
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// 开启 OpenSSL 详细错误信息
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
'capture_peer_cert' => true, // 捕获证书
'capture_peer_cert_chain' => true, // 捕获证书链
// 'cafile' => '/path/to/ca-bundle.crt',
// 'local_cert' => '/path/to/client-cert.pem',
// 'local_pk' => '/path/to/client-key.pem',
// 'passphrase' => 'your_passphrase',
// 'ciphers' => 'HIGH:!aNULL:!MD5',
// 'SNI_enabled' => true,
// 'peer_name' => 'example.com',
]
]);
try {
$fp = fopen('https://api.example.com/endpoint', 'r', false, $context);
// 获取连接的参数
$params = stream_context_get_params($fp);
echo "===== TLS 连接信息 =====\n";
echo "协议版本: " . $params['options']['ssl']['protocol'] ?? 'N/A' . "\n";
echo "密码套件: " . $params['options']['ssl']['cipher'] ?? 'N/A' . "\n";
// 检查证书
if (isset($params['options']['ssl']['peer_certificate'])) {
$cert = openssl_x509_parse($params['options']['ssl']['peer_certificate']);
echo "===== 服务器证书信息 =====\n";
echo "主题: " . $cert['subject']['CN'] . "\n";
echo "颁发者: " . $cert['issuer']['CN'] . "\n";
echo "有效从: " . date('Y-m-d', $cert['validFrom_time_t']) . "\n";
echo "有效到: " . date('Y-m-d', $cert['validTo_time_t']) . "\n";
}
// 读取内容
$response = stream_get_contents($fp);
echo "===== 响应内容 =====\n";
echo $response . "\n";
fclose($fp);
} catch (Exception $e) {
echo "===== 错误信息 =====\n";
echo "错误消息: " . $e->getMessage() . "\n";
echo "错误代码: " . $e->getCode() . "\n";
echo "错误文件: " . $e->getFile() . ":" . $e->getLine() . "\n";
// 获取详细错误
if (function_exists('openssl_error_string')) {
echo "===== OpenSSL 错误 =====\n";
while ($msg = openssl_error_string()) {
echo $msg . "\n";
}
}
}
?>
使用 cURL 进行详细调试
<?php
function debugSSLConnection($url, $options = []) {
$ch = curl_init($url);
// 基础配置
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_VERBOSE => true, // 显示详细输出
CURLOPT_SSL_VERIFYPEER => true, // 验证证书
CURLOPT_SSL_VERIFYHOST => 2, // 验证主机名
CURLOPT_CERTINFO => true, // 获取证书详情
// CURLOPT_CAINFO => '/path/to/ca-bundle.crt',
// CURLOPT_SSLCERT => '/path/to/client-cert.pem',
// CURLOPT_SSLKEY => '/path/to/client-key.pem',
// CURLOPT_SSL_CIPHER_LIST => 'TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384',
// CURLOPT_SSL_OPTIONS => CURLSSLOPT_NO_REVOKE,
// CURLOPT_PROXY => '127.0.0.1:8888', // 代理调试
// CURLOPT_STDERR => fopen('curl_debug.log', 'w'),
]);
// 合并自定义选项
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
// 获取详细信息
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) {
// 可以在这里记录响应头
return strlen($header);
});
$response = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
echo "===== cURL 调试信息 =====\n";
echo "错误号: $errno\n";
echo "错误信息: $error\n";
echo "HTTP 状态码: " . $info['http_code'] . "\n";
echo "SSL 版本: " . $info['ssl_version'] . "\n";
echo "SSL 验证结果: " . $info['ssl_verify_result'] . "\n";
echo "协议: " . $info['protocol'] . "\n";
echo "主 IP: " . $info['primary_ip'] . ":" . $info['primary_port'] . "\n";
if (isset($info['certinfo'])) {
echo "\n===== 证书信息 =====\n";
foreach ($info['certinfo'] as $cert) {
echo "主题: " . $cert['Subject'] . "\n";
echo "颁发者: " . $cert['Issuer'] . "\n";
echo "到期日: " . $cert['Expire date'] . "\n";
echo "---\n";
}
}
// 显示 TLS 握手详情
if ($response === false) {
echo "\n===== TLS 握手失败 =====\n";
if ($errno == CURLE_SSL_CONNECT_ERROR) {
echo "SSL 连接失败,可能原因:\n";
echo "1. 证书无效或过期\n";
echo "2. 证书不匹配域名\n";
echo "3. 服务器不支持当前 TLS 版本\n";
echo "4. 密码套件不匹配\n";
}
}
curl_close($ch);
return [
'success' => $response !== false,
'response' => $response,
'info' => $info
];
}
// 测试连接
$result = debugSSLConnection('https://www.example.com');
?>
使用 stream context 调试
<?php
function debugStreamSSL($url) {
// 定义错误和警告处理
$errors = [];
$warnings = [];
set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$errors) {
$errors[] = "Error $errno: $errstr in $errfile:$errline";
return true;
});
// 创建上下文
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
'capture_peer_cert' => true,
'capture_peer_chain' => true,
'verify_depth' => 5,
'SNI_enabled' => true,
],
'http' => [
'method' => 'GET',
'timeout' => 30,
'follow_location' => true,
'ignore_errors' => true,
]
]);
// 记录 TLS 日志
$tlsLog = [];
stream_context_set_option($context, 'ssl', 'verify_peer', true);
// 尝试连接
$startTime = microtime(true);
$result = @file_get_contents($url, false, $context);
$endTime = microtime(true);
// 恢复错误处理
restore_error_handler();
echo "===== Stream 连接诊断 =====\n";
echo "URL: $url\n";
echo "连接时间: " . round($endTime - $startTime, 4) . " 秒\n";
if ($result === false) {
echo "连接失败!\n";
if (!empty($errors)) {
echo "\n===== 错误列表 =====\n";
foreach ($errors as $error) {
echo $error . "\n";
}
}
// 获取底层 SSL 错误
if (function_exists('openssl_error_string')) {
echo "\n===== OpenSSL 错误流 =====\n";
while ($err = openssl_error_string()) {
echo $err . "\n";
}
}
} else {
echo "连接成功!\n";
// 获取连接参数
$metadata = stream_get_meta_data($result);
if (isset($metadata['crypto'])) {
echo "\n===== 加密参数 =====\n";
print_r($metadata['crypto']);
}
// 获取证书
$params = stream_context_get_params($context);
if (isset($params['options']['ssl']['peer_certificate'])) {
$cert = openssl_x509_parse($params['options']['ssl']['peer_certificate']);
echo "\n===== 服务器证书 =====\n";
echo "CN: " . $cert['subject']['CN'] . "\n";
echo "O: " . ($cert['subject']['O'] ?? 'N/A') . "\n";
echo "Valid from: " . date('Y-m-d', $cert['validFrom_time_t']) . "\n";
echo "Valid to: " . date('Y-m-d', $cert['validTo_time_t']) . "\n";
}
}
}
debugStreamSSL('https://www.example.com');
?>
使用 OpenSSL 命令行工具对比调试
# 查看远程服务器的证书信息 openssl s_client -connect example.com:443 -showcerts # 指定 TLS 版本 openssl s_client -connect example.com:443 -tls1_2 openssl s_client -connect example.com:443 -tls1_3 # 查看支持的密码套件 openssl ciphers -v 'HIGH:!aNULL:!MD5' # 测试特定密码套件 openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' # 查看证书链 openssl s_client -connect example.com:443 -showcerts -CApath /etc/ssl/certs/
使用 PHP OpenSSL 扩展直接调试
<?php
function debugOpenSSL($host, $port = 443) {
echo "===== 开启 SSL 调试 =====\n";
// 查看支持的协议
$supportedProtocols = stream_get_transports();
if (in_array('tls', $supportedProtocols)) {
echo "支持 TLS 传输\n";
}
if (in_array('ssl', $supportedProtocols)) {
echo "支持 SSL 传输\n";
}
// 检查常用函数
echo "\n检查模块状态:\n";
echo "openssl 扩展: " . (extension_loaded('openssl') ? '已加载' : '未加载') . "\n";
echo "openssl 版本: " . (defined('OPENSSL_VERSION_TEXT') ? OPENSSL_VERSION_TEXT : '未知') . "\n";
// 获取 OpenSSL 配置
echo "\nOpenSSL 配置:\n";
echo "默认 CA 文件: " . ini_get('openssl.cafile') . "\n";
echo "默认 CA 路径: " . ini_get('openssl.capath') . "\n";
// 测试连接
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
]
]);
$sock = @stream_socket_client(
"tls://$host:$port",
$errno,
$errstr,
30,
STREAM_CLIENT_CONNECT,
$context
);
if ($sock) {
echo "\n===== TLS 连接成功 =====\n";
echo "主机: $host:$port\n";
// 获取服务器证书
$params = stream_context_get_params($sock);
if (isset($params['options']['ssl']['peer_certificate'])) {
$cert = openssl_x509_parse($params['options']['ssl']['peer_certificate']);
echo "\n证书详情:\n";
print_r($cert['subject']);
echo "\n签名算法: " . $cert['signatureTypeLN'] . "\n";
}
fclose($sock);
} else {
echo "\n===== 连接失败 =====\n";
echo "错误号: $errno\n";
echo "错误信息: $errstr\n";
// 显示 OpenSSL 错误
while ($sslError = openssl_error_string()) {
echo "SSL 错误: $sslError\n";
}
}
}
debugOpenSSL('www.example.com');
?>
常见 SSL/TLS 问题及解决方法
<?php
function diagnoseSSLIssues($url) {
echo "===== SSL/TLS 问题诊断 =====\n\n";
// 1. 检查证书过期
$cert = @file_get_contents($url, false, stream_context_create([
'ssl' => ['verify_peer' => false]
]));
// 2. 常见问题列表
echo "常见 SSL/TLS 问题及检查方法:\n";
echo "1. 证书过期: 检查证书有效期\n";
echo " - 使用: openssl s_client -connect host:443 查看\n";
echo " - 检查服务器系统时间\n\n";
echo "2. 证书链问题: 确保服务器提供完整证书链\n";
echo " - 使用: openssl s_client -connect host:443 -showcerts\n";
echo " - 中间证书可能缺失\n\n";
echo "3. 域名不匹配: 证书 CN 或 SAN 不包含您的域名\n";
echo " - 检查证书中的域名\n";
echo " - 考虑通配符证书\n\n";
echo "4. TLS 版本不兼容: 服务器或客户端不支持共同的 TLS 版本\n";
echo " - 检查服务器支持的 TLS 版本\n";
echo " - 使用 -tls1_2 或 -tls1_3 参数\n\n";
echo "5. 密码套件问题: 无共同的加密算法支持\n";
echo " - 使用: openssl ciphers -v 'HIGH'\n";
echo " - 检查服务器支持的密码套件\n\n";
echo "6. 客户端证书问题: 双向认证时缺少或不正确\n";
echo " - 检查 SSLCert 和 SSLKey 路径\n";
echo " - 检查证书格式 (PEM/DER)\n\n";
echo "7. 代理问题: 代理拦截 SSL 连接\n";
echo " - 在本地测试无代理情况下是否成功\n";
echo " - 查看代理日志\n\n";
echo "8. SNI 问题: 服务器上的虚拟主机配置\n";
echo " - 确保 SNI_enabled => true\n";
echo " - 检查 peer_name 设置\n";
}
?>
最佳实践建议
<?php
// 生产环境安全配置
$secureContext = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
'verify_depth' => 10,
'SNI_enabled' => true,
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT |
STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
'ciphers' => 'HIGH:!aNULL:!MD5:!3DES:!DES:!RC4',
'peer_fingerprint' => null, // 可选的证书指纹验证
]
]);
// 开发环境调试配置(仅用于测试,生产环境禁用!)
$debugContext = stream_context_create([
'ssl' => [
'verify_peer' => false, // 生产环境必须改为 true
'verify_peer_name' => false,
'allow_self_signed' => true,
'crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
]
]);
?>
-
第一步:基础检查
- PHP OpenSSL 扩展是否加载
- 系统 CA 证书是否更新
-
第二步:连接测试
- 使用
openssl s_client测试服务器 - 对比验证 PHP 和命令行行为
- 使用
-
第三步:详细错误捕获
- 开启所有错误报告
- 捕获 OpenSSL 错误流
-
第四步:证书检查
- 使用
openssl x509验证证书 - 检查证书链完整性
- 使用
-
第五步:环境优化
- 更新 CA 证书
- 配置正确的密码套件
- 设置合适的 TLS 版本
生产环境永远要开启 verify_peer 和 verify_peer_name,调试用的宽松设置绝不能用于生产!
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播